Spring 5.0.0框架介绍_中英文对照_持续更新

文章作者:Tyan
博客:noahsnail.com

Part I. Overview of Spring Framework

The Spring Framework is a lightweight solution and a potential one-stop-shop for building your enterprise-ready applications. However, Spring is modular, allowing you to use only those parts that you need, without having to bring in the rest. You can use the IoC container, with any web framework on top, but you can also use only theHibernate integration code or the JDBC abstraction layer. The Spring Framework supports declarative transaction management, remote access to your logic through RMI or web services, and various options for persisting your data. It offers a full-featured MVC framework, and enables you to integrate AOP transparently into your software.

Spring框架是一个轻量级的解决方案,对于构建一个企业级应用来说,Spring框架也是一种可能的一站式服务。Spring是模块化的,允许你仅使用你需要的那部分功能,而不必引入其它的部分。你可以在任何web框架上使用IoC容器,也可以只使用Hibernate集成代码或JDBC抽象层。Spring框架支持声明式的业务管理,支持通过RMI或web service来远程访问你的逻辑,并且支持多种持久化数据的方式。Spring提供了一个包含所有功能的MVC框架,使你能将AOP透明的集成到软件中。

Spring is designed to be non-intrusive, meaning that your domain logic code generally has no dependencies on the framework itself. In your integration layer (such as the data access layer), some dependencies on the data access technology and the Spring libraries will exist. However, it should be easy to isolate these dependencies from the rest of your code base.

Spring被设计为非侵入式的,这意味着你自己的逻辑代码通常情况下不依赖于框架本身。在你的集成层(例如数据访问层),将会存在一些数据访问技术的依赖和Spring的库。不管怎样,从你其余的代码中分离这些依赖应该是很容易的。

This document is a reference guide to Spring Framework features. If you have any requests, comments, or questions on this document, please post them on the user mailing list. Questions on the Framework itself should be asked on StackOverflow (see https://spring.io/questions).

这篇文档是Spring框架功能的参考手册。如果你有任何关于这篇文档的要求、评论或问题,请向用户邮寄列表中的人发邮件。关于框架本身的问题可以在StackOverflow上提问。

1.Getting Started with Spring

This reference guide provides detailed information about the Spring Framework. It provides comprehensive documentation for all features, as well as some background about the underlying concepts (such as “Dependency Injection”) that Spring has embraced.

这本参考手册提供了关于Spring框架的详细信息,它提供了关于所有功能的全面文档,也介绍了Spring中的基本概念(例如依赖注入)的一些背景。

If you are just getting started with Spring, you may want to begin using the Spring Framework by creating a Spring Boot based application. Spring Boot provides a quick (and opinionated) way to create a production-ready Spring based application. It is based on the Spring Framework, favors convention over configuration, and is designed to get you up and running as quickly as possible.

如果你刚开始学习Spring,你可能想创建一个基于Spring Boot的应用,Spring Boot提供了一个快速(和武断的)方式来创建一个用于生产环境的基于Spring的应用。它是基于Spring框架的,支持约定大于配置,被设计为可以快速启动并且尽可能快的运行起来。

You can use start.spring.io to generate a basic project or follow one of the “Getting Started” guides like the Getting Started Building a RESTful Web Service one. As well as being easier to digest, these guides are very task focused, and most of them are based on Spring Boot. They also cover other projects from the Spring portfolio that you might want to consider when solving a particular problem.

你可以用start.spring.io 来生产一个基本的工程或遵循『Getting Started』指南中的一个,例如『Started Building a RESTful Web Service』指南。除了容易理解吸收之外,这些指南主要是基于任务的,它们中的大多数是基于Spring Boot的。它们也包含了Spring的其它工程,当解决一个特定问题时你可能会考虑它们。

2.Introduction to the Spring Framework

The Spring Framework is a Java platform that provides comprehensive infrastructure support for developing Java applications. Spring handles the infrastructure so you can focus on your application.

Spring框架是一个为支持开发Java应用提供全面基础架构的Java平台。Spring处理基础架构,因此你可以集中精力在你有应用上。

Spring enables you to build applications from “plain old Java objects” (POJOs) and to apply enterprise services non-invasively to POJOs. This capability applies to the Java SE programming model and to full and partial Java EE.

Spring使你能创建普通Java对象(POJO)并能非侵入式的将企业服务应用到普通Java对象(POJO)上。

Examples of how you, as an application developer, can benefit from the Spring platform:

  • Make a Java method execute in a database transaction without having to deal with transaction APIs.
  • Make a local Java method a remote procedure without having to deal with remote APIs.
  • Make a local Java method a management operation without having to deal with JMX APIs.
  • Make a local Java method a message handler without having to deal with JMS APIs.

作为一个应用开发者,下面是一些你能从Spring平台受益的例子:

  • 在一个数据库业务中执行一个Java方法而不必处理业务APIs
  • 使一个本地的Java方法可以远程调用而不必处理远程APIs
  • 使一个本地Java方法变为管理操作而不必处理JMX APIs
  • 使一个本地Java方法变为消息处理器而不必处理JMS APIs

2.1 Dependency Injection and Inversion of Control

A Java application — a loose term that runs the gamut from constrained, embedded applications to n-tier, server-side enterprise applications — typically consists of objects that collaborate to form the application proper. Thus the objects in an application have dependencies on each other.

Java应用——一个不精确的术语,既可以表示受限制的嵌入式应用又可以表示N层服务端的企业级应用——通常由许多对象构成,这些对象协作形成完整的应用程序。因此一个应用程序中的对象是相互依赖的。

Although the Java platform provides a wealth of application development functionality, it lacks the means to organize the basic building blocks into a coherent whole, leaving that task to architects and developers. Although you can use design patterns such as Factory, Abstract Factory, Builder, Decorator, and Service Locator to compose the various classes and object instances that make up an application, these patterns are simply that: best practices given a name, with a description of what the pattern does, where to apply it, the problems it addresses, and so forth. Patterns are formalized best practices that you must implement yourself in your application.

尽管Java平台提供了大量的应用开发功能,但是它缺少把这些基本构建模块组织成一个连贯整体的方法,并把组织基本构建模块的任务留给了架构师和开发者。虽然你可以使用设计模式例如工厂模式、抽象工厂模式、生成器模式、装饰模式、服务定位模式来创建构成应用的各种类和对象实例,但这些设计模式很简单:命名的最佳方法、模式的作用描述、应用模式的位置、模式解决的问题等等。模式使最佳实践形式化了,这意味着你必须在你的应用中自己实现它

The Spring Framework Inversion of Control (IoC) component addresses this concern by providing a formalized means of composing disparate components into a fully working application ready for use. The Spring Framework codifies formalized design patterns as first-class objects that you can integrate into your own application(s). Numerous organizations and institutions use the Spring Framework in this manner to engineer robust, maintainable applications.

Spring框架中的控制反转(IoC)组件通过提供一种形式化方法解决了这个问题,这个形式化方法将不同的组件创建到一个随时可用的完整的工作应用中。Spring框架将形式化的设计模式编码成了你可以集成到你自己的应用中的最好对象。许多组织和机构用这种方式应用Spring框架来构建鲁棒的、可维护的应用。

Background
“The question is, what aspect of control are [they] inverting?” Martin Fowler posed this question about Inversion of Control (IoC) on his site in 2004. Fowler suggested renaming the principle to make it more self-explanatory and came up with Dependency Injection.

 

背景
“问题是什么是控制反转?” 2004年Martin Fowler在他的网站上提出了这个关于控制反转(IoC)问题。Fowler建议重新命名这个原理使它更一目了然并且提出了依赖注入

2.2 Modules

The Spring Framework consists of features organized into about 20 modules. These modules are grouped into Core Container, Data Access/Integration, Web, AOP (Aspect Oriented Programming), Instrumentation, Messaging, and Test, as shown in the following diagram.

Spring框架包含的功能大约由20个模块组成。这些模块按组可分为核心容器、数据访问/集成,Web,AOP(面向切面编程)、设备、消息和测试,如下图所示。

Figure 2.1. Overview of the Spring Framework
image

The following sections list the available modules for each feature along with their artifact names and the topics they cover. Artifact names correlate to artifact IDs used in Dependency Management tools.

接下来的章节列出了每个功能可用的模块、它们的工件名字以及它们包含的主题。工件名字与依赖管理工具中使用的artifact IDs有关。

2.2.1 Core Container

The Core Container consists of the spring-core, spring-beans, spring-context, spring-context-support, and spring-expression (Spring Expression Language) modules.

核心容器功能包括spring-core, spring-beans, spring-context, spring-context-support, and spring-expression(Spring表现语言)模块。

The spring-core and spring-beans modules provide the fundamental parts of the framework, including the IoC and Dependency Injection features. The BeanFactory is a sophisticated implementation of the factory pattern. It removes the need for programmatic singletons and allows you to decouple the configuration and specification of dependencies from your actual program logic.

spring-corespring-beans模块提供了框架的基础结构部分,包含控制反转(IoC)和依赖注入(DI)功能。BeanFactory是工厂模式的高级实现。它去掉了程序单例模式的需求并且允许你从实际的程序逻辑中解耦配置和依赖关系。

The Context (spring-context) module builds on the solid base provided by the Core and Beans modules: it is a means to access objects in a framework-style manner that is similar to a JNDI registry. The Context module inherits its features from the Beans module and adds support for internationalization (using, for example, resource bundles), event propagation, resource loading, and the transparent creation of contexts by, for example, a Servlet container. The Context module also supports Java EE features such as EJB, JMX, and basic remoting. The ApplicationContext interface is the focal point of the Context module.spring-context-support provides support for integrating common third-party libraries into a Spring application context, in particular for caching (EhCache, JCache) and scheduling (CommonJ, Quartz).

上下文(spring-context)模块建立在由Core模块和Beans模块提供的坚实基础上:它是在类似于JNDI注册表式的框架风格模式中访问对象的一种方法。上下文模块继承了Beans模块的功能,并添加了对国际化(例如使用资源捆绑)、事件传播、资源加载和上下文透明创建(例如通过Servlet容器)的支持。上下文模块也支持Java EE功能例如EJB,JMX和基本的远程。ApplicationContext接口是上下文模块的焦点。spring-context-support支持将第三方库集成进Spring应用程序上下文中,特别是缓存(EhCache, JCache)和定时执行(CommonJ, Quartz)。

The spring-expression module provides a powerful Expression Language for querying and manipulating an object graph at runtime. It is an extension of the unified expression language (unified EL) as specified in the JSP 2.1 specification. The language supports setting and getting property values, property assignment, method invocation, accessing the content of arrays, collections and indexers, logical and arithmetic operators, named variables, and retrieval of objects by name from Spring’s IoC container. It also supports list projection and selection as well as common list aggregations.

spring-expression模块提供了强大的表达式语言用来在运行时查询和操作对象图。它是JSP 2.1规范中统一表达式语言(unified EL)的扩展。这个语言支持setting和getting属性值,属性分配,方法调用,访问数组、集合和索引器的内容,逻辑和算术操作,变量命名,从Spring Ioc容器中通过名字检索对象。它也支持它还支持列表投影、选择以及常见的列表聚合。

2.2.2 AOP and Instrumentation

The spring-aop module provides an AOP Alliance-compliant aspect-oriented programming implementation allowing you to define, for example, method interceptors and pointcuts to cleanly decouple code that implements functionality that should be separated. Using source-level metadata functionality, you can also incorporate behavioral information into your code, in a manner similar to that of .NET attributes.

spring-aop模块提供了AOP Alliance-compliant(AOP联盟)面向切面编程的实现,例如允许你自定义方法拦截器和切入点来清晰的解耦功能实现上应该分开的代码。使用源码级的元数据功能,你也可以将行为信息合并到你的代码中,在某种程度上这类似于.NET的属性值。

The separate spring-aspects module provides integration with AspectJ.

独立的spring-aspects模块提供了与AspectJ的集成。

The spring-instrument module provides class instrumentation support and classloader implementations to be used in certain application servers. The spring-instrument-tomcat module contains Spring’s instrumentation agent for Tomcat.

spring-instrument模块提供了类设备支持和类加载器的实现,它们可以在某些应用服务器中使用。spring-instrument-tomcat模块包含了Tomcat的Spring设备代理。

2.2.3 Messaging

Spring Framework 4 includes a spring-messaging module with key abstractions from the Spring Integration project such as Message, MessageChannel, MessageHandler, and others to serve as a foundation for messaging-based applications. The module also includes a set of annotations for mapping messages to methods, similar to the Spring MVC annotation based programming model.

Spring 4框架中包含了spring-messaging模块,它对Spring集成项目例如Message, MessageChannel, MessageHandler和其它作为消息应用服务基础的项目进行了重要的抽象。这个模块也包含了一系列将消息映射到方法上的注解,这个注解与基于编程模型Spring MVC注解类似。

2.2.4 Data Access/Integration

The Data Access/Integration layer consists of the JDBC, ORM, OXM, JMS, and Transaction modules.

数据访问/集成层包括JDBC,ORM,OXM,JMS和业务模块。

The spring-jdbc module provides a JDBC-abstraction layer that removes the need to do tedious JDBC coding and parsing of database-vendor specific error codes.

spring-jdbc模块提供了JDBC抽象层,不需要再编写单调的JDBC代码,解析数据库提供商指定的错误编码。

The spring-tx module supports programmatic and declarative transaction management for classes that implement special interfaces and for all your POJOs (Plain Old Java Objects).

spring-tx模块为实现指定接口和所有的普通Java对象(POJOs)的类提供编程式(programmatic)和声明式(declarative)的业务管理。

The spring-orm module provides integration layers for popular object-relational mapping APIs, including JPA and Hibernate. Using the spring-orm module you can use these O/R-mapping frameworks in combination with all of the other features Spring offers, such as the simple declarative transaction management feature mentioned previously.

spring-orm模块提供流行的对象关系映射APIs的集成层,包括JPA和Hibernate。在使用spring-orm模块时,你可以将Spring的其它功能与这些O/R-mapping框架结合起来使用,例如前面提到的简单声明式业务管理的功能。

The spring-oxm module provides an abstraction layer that supports Object/XML mapping implementations such as JAXB, Castor, JiBX and XStream.

spring-oxm模块提供对Object/XML映射实现例如JAXB,Castor,JiBx和XStream的抽象层。

The spring-jms module (Java Messaging Service) contains features for producing and consuming messages. Since Spring Framework 4.1, it provides integration with the spring-messaging module.

spring-jms模块(Java消息服务)包含产生和处理消息的功能。从Spring 4.1框架开始它提供了与spring-messaging的集成。

2.2.5 Web

The Web layer consists of the spring-web, spring-webmvc and spring-websocket modules.

网络层包含spring-web, spring-webmvcspring-websocket模块。

The spring-web module provides basic web-oriented integration features such as multipart file upload functionality and the initialization of the IoC container using Servlet listeners and a web-oriented application context. It also contains an HTTP client and the web-related parts of Spring’s remoting support.

spring-web模块提供基本的面向网络集成功能,例如multipart文件上传功能,使用Servlet监听器来初始化Ioc容器和面向网络的应用程序上下文。它也包含了HTTP客户端和Spring远程支持中网络相关的部分。

The spring-webmvc module (also known as the Web-Servlet module) contains Spring’s model-view-controller (MVC) and REST Web Services implementation for web applications. Spring’s MVC framework provides a clean separation between domain model code and web forms and integrates with all of the other features of the Spring Framework.

spring-webmvc模块(也被称为Web-Servlet模块)包含了Spring的model-view-controller(MVC)和REST Web Services的网络应用实现。Spring的MVC框架提供了对领域模型代码,web表单,Spring框架其他功能的完全分离。

2.2.6 Test

The spring-test module supports the unit testing and integration testing of Spring components with JUnit or TestNG. It provides consistent loading of Spring ApplicationContexts and caching of those contexts. It also provides mock objects that you can use to test your code in isolation.

spring-test模块支持单元测试,Spring组件和JUnit或TestNG的集成测试。它提供了Spring的ApplicationContexts加载和这些上下文缓存的一致。它也提供了可以单独测试代码的模拟对象。

2.3 Usage scenarios

The building blocks described previously make Spring a logical choice in many scenarios, from embedded applications that run on resource-constrained devices to full-fledged enterprise applications that use Spring’s transaction management functionality and web framework integration.

前面描述的搭积木方式使Spring在许多场景中都有一个合理选择,从运行在资源受限的嵌入式应用到全面成熟的企业级应用都在使用Spring的业务管理功能和网络框架集成。

Figure 2.2. Typical full-fledged Spring web application
image

Spring’s declarative transaction management features make the web application fully transactional, just as it would be if you used EJB container-managed transactions. All your custom business logic can be implemented with simple POJOs and managed by Spring’s IoC container. Additional services include support for sending email and validation that is independent of the web layer, which lets you choose where to execute validation rules. Spring’s ORM support is integrated with JPA and Hibernate; for example, when using Hibernate, you can continue to use your existing mapping files and standard Hibernate SessionFactory configuration. Form controllers seamlessly integrate the web-layer with the domain model, removing the need for ActionForms or other classes that transform HTTP parameters to values for your domain model.

Spring的声明式业务管理功能使web应用全面的业务化,如果你用过EJB容器管理业务的话你会发现它们基本一样。你所有自定义的业务逻辑都可以用POJOs实现并通过Spring的IoC容器管理。附加业务包括支持邮件发送和验证,这个是独立于web层之外的,你可以自由选择验证规则执行的位置。Spring对ORM的支持与JPA和Hibernate进行了集成;例如,当你使用Hibernate时,你可以继续使用你现有的映射文件和标准的Hibernate SessionFactory配置。表单控制器被无缝的将web层和领域模型进行了集成,对于你的领域模型来讲不再需要ActionForms或其它的将HTTP参数转换成值的

Figure 2.3. Spring middle-tier using a third-party web framework
image

Sometimes circumstances do not allow you to completely switch to a different framework. The Spring Framework does not force you to use everything within it; it is not an all-or-nothing solution. Existing front-ends built with Struts, Tapestry, JSF or other UI frameworks can be integrated with a Spring-based middle-tier, which allows you to use Spring transaction features. You simply need to wire up your business logic using an ApplicationContext and use a WebApplicationContext to integrate your web layer.

有时候环境不允许你完全转成一个不同的框架。Spring框架强迫你都采用它内部的东西;它不是一个要么全有要么全无的解决方案。现有的采用Struts,Tapestry,JSF或其它UI框架构建的前端可以与基于Spring的中间层进行集成,这可以让你使用Spring的业务功能。你只需要简单的用ApplicationContextWebApplicationContext绑定你的业务逻辑然后集成到web层即可。

Figure 2.4. Remoting usage scenario
image

When you need to access existing code through web services, you can use Spring’s Hessian-, Rmi- or HttpInvokerProxyFactoryBean classes. Enabling remote access to existing applications is not difficult.

当你需要通过web服务访问现有代码时,你可以使用Spring的Hessian-, Rmi-HttpInvokerProxyFactoryBean类。这能让远程访问现有应用变得很容易。

Figure 2.5. EJBs - Wrapping existing POJOs
image

The Spring Framework also provides an access and abstraction layer for Enterprise JavaBeans, enabling you to reuse your existing POJOs and wrap them in stateless session beans for use in scalable, fail-safe web applications that might need declarative security.

Spring框架也为企业JavaBeans提供了访问和抽象层,使你能重用你现有的POJOs,为了可扩展使用可以将它们包装成无状态的session beans,自动防故障的web应用可能需要声明安全。

2.3.1 Dependency Management and Naming Conventions

Dependency management and dependency injection are different things. To get those nice features of Spring into your application (like dependency injection) you need to assemble all the libraries needed (jar files) and get them onto your classpath at runtime, and possibly at compile time. These dependencies are not virtual components that are injected, but physical resources in a file system (typically). The process of dependency management involves locating those resources, storing them and adding them to classpaths. Dependencies can be direct (e.g. my application depends on Spring at runtime), or indirect (e.g. my application depends on commons-dbcp which depends on commons-pool). The indirect dependencies are also known as “transitive” and it is those dependencies that are hardest to identify and manage.

依赖管理和依赖注入是完全不同的两件事。为了能你的应用中使用Spring的优秀特性(像依赖注入),你需要收集所有必要的库(jar文件)并在运行时将它们添加到classpath中,有可能在编译时就需要添加。这些依赖不是要被注入的虚拟组建,而是文件系统中的物理资源(通常情况下)。这些依赖管理的过程包括资源的定位、存储和添加到classpath中。依赖可以是直接的(例如:我的应用在运行时依赖Spring),或间接的(例如:我的应用依赖commons-dbcp,而它依赖commons-pool)。间接依赖也被称为”传递式”的,这些依赖也是最难识别和管理的。

If you are going to use Spring you need to get a copy of the jar libraries that comprise the pieces of Spring that you need. To make this easier Spring is packaged as a set of modules that separate the dependencies as much as possible, so for example if you don’t want to write a web application you don’t need the spring-web modules. To refer to Spring library modules in this guide we use a shorthand naming convention spring-* or spring-*.jar, where * represents the short name for the module (e.g. spring-core, spring-webmvc, spring-jms, etc.). The actual jar file name that you use is normally the module name concatenated with the version number (e.g. spring-core-5.0.0.BUILD-SNAPSHOT.jar).

如果你想使用Spring,你需要有包含你需要的Spirng功能的jar库副本。为了使这个更容易,Spring被打包成了一系列尽可能将依赖分离开的模块,例如你不想写web应用那你就不需要spring-web模块。为了在本指南中谈及Spring的库模块,我们使用了一个简写命名约定spring-*spring-*.jar*表示模块的简写名字(例如spring-core, spring-webmvc, spring-jms等等)。实际中你使用的jar文件名字通常是模块名加上版本号(例如spring-core-5.0.0.BUILD-SNAPSHOT.jar)。

Each release of the Spring Framework will publish artifacts to the following places:

  • Maven Central, which is the default repository that Maven queries, and does not require any special configuration to use. Many of the common libraries that Spring depends on also are available from Maven Central and a large section of the Spring community uses Maven for dependency management, so this is convenient for them. The names of the jars here are in the form spring-*-<version>.jar and the Maven groupId is org.springframework.
  • In a public Maven repository hosted specifically for Spring. In addition to the final GA releases, this repository also hosts development snapshots and milestones. The jar file names are in the same form as Maven Central, so this is a useful place to get development versions of Spring to use with other libraries deployed in Maven Central. This repository also contains a bundle distribution zip file that contains all Spring jars bundled together for easy download.
    So the first thing you need to decide is how to manage your dependencies: we generally recommend the use of an automated system like Maven, Gradle or Ivy, but you can also do it manually by downloading all the jars yourself.

Spring框架的每次发布都会下面的地方公布artifacts:

  • Maven Central,Maven查询的默认仓库,使用时不需要任何特定的配置。Spring依赖的许多共通库也可以从Maven Central获得,Spring社区的很大一部分都在使用Maven进行依赖管理,因此这对他们来说是很方便的。jar包的命名形式是spring-*-<version>.jar,Maven GroupId是org.springframework
  • 由Spring掌管的公开Maven库。除了最终的GA release(公开可获得的版本)之外,这个仓库也有开发版本的快照和milestone版本。jar包的命名形式和Maven Central一样,这是一个可以使用Spring开发版本有用地方,而其它的库部署在Maven Central。这个库也包含的捆绑分布的zip文件,这个zip文件中所有的Spring jar包被捆绑到一起很容易下载。

You will find bellow the list of Spring artifacts. For a more complete description of each modules, see Section 2.2, “Modules”.

你将在下面找到Spring artifacts列表。想要每个模块更全面的描述,请看2.2小节。

Table 2.1. Spring Framework Artifacts
image

Spring Dependencies and Depending on Spring

Although Spring provides integration and support for a huge range of enterprise and other external tools, it intentionally keeps its mandatory dependencies to an absolute minimum: you shouldn’t have to locate and download (even automatically) a large number of jar libraries in order to use Spring for simple use cases. For basic dependency injection there is only one mandatory external dependency, and that is for logging (see below for a more detailed description of logging options).

虽然Spring提供集成并支持大范围内的企业和其它外部工具,但它有意使它的强制性依赖到一个绝对最小化的程度:对于简单的用例你不应该为了使用Spring而定位和下载(即使是自动的)许多jar库。对于基本的依赖注入仅有一个强制性的外部依赖,那个依赖是关于日志的(在下面可以看到日志选择更详细的描述)。

Next we outline the basic steps needed to configure an application that depends on Spring, first with Maven and then with Gradle and finally using Ivy. In all cases, if anything is unclear, refer to the documentation of your dependency management system, or look at some sample code - Spring itself uses Gradle to manage dependencies when it is building, and our samples mostly use Gradle or Maven.

接下来我们概述配置一个依赖于Spring的应用需要的基本步骤,首先Maven的,其次是Gradle的,最后是Ivy的。在所有的案例中,如果有任何不清楚的地方,请参考你的依赖管理系统的文档,或者看一些示例代码——Spring本身构建时使用Gradle来管理依赖,我们例子中大多数是使用Gradle和Maven的。

Maven Dependency Management

If you are using Maven for dependency management you don’t even need to supply the logging dependency explicitly. For example, to create an application context and use dependency injection to configure an application, your Maven dependencies will look like this:

如果你正在使用Maven来进行依赖管理,那你不必显式的提供日志依赖。例如,为了创建一个应用上下文,使用依赖注入来配置一个应用,你的Maven依赖看上去是这样的:

1
2
3
4
5
6
7
8
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.0.BUILD-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
</dependencies>

That’s it. Note the scope can be declared as runtime if you don’t need to compile against Spring APIs, which is typically the case for basic dependency injection use cases.

就是它。注意如果你不需要编译Spring APIs,scope可以被声明成rumtime,这是典型的基本依赖注入的情况。

The example above works with the Maven Central repository. To use the Spring Maven repository (e.g. for milestones or developer snapshots), you need to specify the repository location in your Maven configuration. For full releases:

上面的例子是采用Maven中心仓库的。为了使用Spring Maven仓库(例如:使用milestone版本或snapshot版本),你需要在Maven配置中指定仓库的位置,完整的版本:

1
2
3
4
5
6
7
<repositories>
<repository>
<id>io.spring.repo.maven.release</id>
<url>http://repo.spring.io/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>

For milestones:
对于milestone版本:

1
2
3
4
5
6
7
<repositories>
<repository>
<id>io.spring.repo.maven.milestone</id>
<url>http://repo.spring.io/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>

And for snapshots:
对于snapshot版本:

1
2
3
4
5
6
7
<repositories>   
<repository>
<id>io.spring.repo.maven.snapshot</id>
<url>http://repo.spring.io/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>

Maven “Bill Of Materials” Dependency

It is possible to accidentally mix different versions of Spring JARs when using Maven. For example, you may find that a third-party library, or another Spring project, pulls in a transitive dependency to an older release. If you forget to explicitly declare a direct dependency yourself, all sorts of unexpected issues can arise.

在使用Maven时,有可能会偶然的将不同版本的Spring JARs混合起来。例如,你可能找到一个第三方库,或另一个Spring项目,通过传递依赖进入了一个更旧的版本。如果你忘了自己显式的声明一个直接依赖,会产生各种意想不到的问题。

To overcome such problems Maven supports the concept of a “bill of materials” (BOM) dependency. You can import the spring-framework-bom in your dependencyManagement section to ensure that all spring dependencies (both direct and transitive) are at the same version.

为了解决这种问题,Maven支持”材料清单”(BOM)依赖的概念。你可以在你的dependencyManagement部分导入spring-framework-bom来确保所有的Spring依赖(直接和传递的)都是同一个版本。

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.0.0.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

An added benefit of using the BOM is that you no longer need to specify the <version> attribute when depending on Spring Framework artifacts:

使用BOM的额外好处是当依赖Spring框架的artifacts时你不再需要指定<version>属性:

1
2
3
4
5
6
7
8
9
10
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies>

Gradle Dependency Management

To use the Spring repository with the Gradle build system, include the appropriate URL in the repositories section:

为了在Gradle构建系统中使用Spring仓库,在repositories部分需要包含合适的URL:

1
2
3
4
5
repositories {
mavenCentral()
// and optionally...
maven { url "http://repo.spring.io/release" }
}

You can change the repositories URL from /release to /milestone or /snapshot as appropriate. Once a repository has been configured, you can declare dependencies in the usual Gradle way:

当合适的时候你可以修改repositories的URL从/release/milestone/snapshot。一旦一个仓库被配置了,你可以用通常的Gradle方式声明依赖:

1
2
3
4
dependencies {
compile("org.springframework:spring-context:5.0.0.BUILD-SNAPSHOT")
testCompile("org.springframework:spring-test:5.0.0.BUILD-SNAPSHOT")
}

Ivy Dependency Management

If you prefer to use Ivy to manage dependencies then there are similar configuration options.

如果你更喜欢使用Ivy来管理依赖,这有类似的配置选择。

To configure Ivy to point to the Spring repository add the following resolver to your ivysettings.xml:

为了配置Ivy指定Spring仓库,添加下面的解析器到你的ivysettings.xml:

1
2
3
4
5
<resolvers>
<ibiblio name="io.spring.repo.maven.release"
m2compatible="true"
root="http://repo.spring.io/release/"/>
</resolvers>

You can change the root URL from /release/ to /milestone/ or /snapshot/ as appropriate.

当合适的时候你可以更改根URL从repositories的URL从/release/milestone/snapshot

Once configured, you can add dependencies in the usual way. For example (in ivy.xml):

一旦配置了,你可以通过一般的方式添加依赖。例如(在ivy.xml):

1
2
<dependency org="org.springframework"
name="spring-core" rev="5.0.0.BUILD-SNAPSHOT" conf="compile->runtime"/>

Distribution Zip Files

Although using a build system that supports dependency management is the recommended way to obtain the Spring Framework, it is still possible to download a distribution zip file.

尽管使用一个支持依赖管理的构建系统是获得Spring框架的推荐方式,但仍然可以下载发行版的Zip文件。

Distribution zips are published to the Spring Maven Repository (this is just for our convenience, you don’t need Maven or any other build system in order to download them).

发行版的zips是被发布到Spring Maven仓库(这只是为了我们的方便,为了下载它们你不需要Maven或任何其它的构建系统)。

To download a distribution zip open a web browser to http://repo.spring.io/release/org/springframework/spring and select the appropriate subfolder for the version that you want. Distribution files end -dist.zip, for example spring-framework-{spring-version}-RELEASE-dist.zip. Distributions are also published for milestones and snapshots.

为了下载发行版zip,打开浏览器输入http://repo.spring.io/release/org/springframework/spring,然后选择你想要的版本的合适子文件夹。发行版文件以-dist.zip结尾,例如spring-framework-{spring-version}-RELEASE-dist.zip。发行版也可以公布milestone版本或snapshots版本。

2.3.2 Logging

Logging is a very important dependency for Spring because a) it is the only mandatory external dependency, b) everyone likes to see some output from the tools they are using, and c) Spring integrates with lots of other tools all of which have also made a choice of logging dependency. One of the goals of an application developer is often to have unified logging configured in a central place for the whole application, including all external components. This is more difficult than it might have been since there are so many choices of logging framework.

日志对于Spring来说是一个非常重要的依赖,因为:a)它是唯一的强制性外部依赖,b)每个人都喜欢从他们使用的工具中看到一些输出,c)Spring集成了许多其它的工具,这些工具也选择了日志依赖。应用开发者的一个目标就是对于整个应用来讲,经常要有一个中心地方来进行日志的统一配置,包括所有的外部组件。比它更困难的可能是有太多的日志框架去选择。

The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We compile against JCL and we also make JCL Log objects visible for classes that extend the Spring Framework. It’s important to users that all versions of Spring use the same logging library: migration is easy because backwards compatibility is preserved even with applications that extend Spring. The way we do this is to make one of the modules in Spring depend explicitly on commons-logging (the canonical implementation of JCL), and then make all the other modules depend on that at compile time. If you are using Maven for example, and wondering where you picked up the dependency on commons-logging, then it is from Spring and specifically from the central module called spring-core.

Spring中的强制日志依赖是Jakarta Commons Logging API (JCL)。我们编译JCL并使JCLlog对象对类是可见的,这扩展了Spring框架。所有版本的Spring采用同一个日志库:移植是容易的,因为即使应用扩展了Spring但保留了向后兼容性,这一点对用户来说很重要。我们实现这个的方式是让Spring的模块之一显式的依赖commons-logging(JCL的标准实现),然后使其它模块在编译时依赖这个模块。例如如果你在使用Maven,想找出依赖于commons-logging的依赖在哪,它在Spring中,更确切的说它是在Spring的中心模块spring-core中。

The nice thing about commons-logging is that you don’t need anything else to make your application work. It has a runtime discovery algorithm that looks for other logging frameworks in well known places on the classpath and uses one that it thinks is appropriate (or you can tell it which one if you need to). If nothing else is available you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short). You should find that your Spring application works and logs happily to the console out of the box in most situations, and that’s important.

关于commons-logging的一件好事是要使你的应用工作你不需要任何其它的东西。它有一个运行时发现算法,这个算法能寻找其它的日志框架在知名的classpath中,并使用一个它认为是合适的(或者你告诉它你想用哪个如果你需要的话)。如果找不到任何别的你可以从JDK中找到一个非常美好漂亮的日志(java.util.logging或缩写为JUL)。在大多数环境中你可以发现你的Spring应用恰当地运行并输出日志到控制台输出框中,那是很重要的。

Not Using Commons Logging

Unfortunately, the runtime discovery algorithm in commons-logging, while convenient for the end-user, is problematic. If we could turn back the clock and start Spring now as a new project it would use a different logging dependency. The first choice would probably be the Simple Logging Facade for Java ( SLF4J), which is also used by a lot of other tools that people use with Spring inside their applications.

不幸的是, 虽然commons-logging的运行时发现算法对于终端用户是方便的,但它是有问题的。如果我们将时钟回拨,把Spring作为一个新项目重新开始,将会选择一个不同的日志依赖。第一个选择可能是Simple Logging Facade for Java(SLF4J),应用内部使用Spring的人使用的许多其它工具也用了SLF4J。

There are basically two ways to switch off commons-logging:

  1. Exclude the dependency from the spring-core module (as it is the only module that explicitly depends on commons-logging)
  2. Depend on a special commons-logging dependency that replaces the library with an empty jar (more details can be found in the SLF4J FAQ)

这儿有两种方式关掉commons-logging:

  1. spring-core模块排除依赖(因为它是唯一的显式依赖)commons-logging的模块
  2. 依赖于一个特定的commons-logging依赖,用一个空jar替换这个依赖(更多细节可以在SLF4J FAQ中找到)。

To exclude commons-logging, add the following to your dependencyManagement section:

为了排除commons-logging,把下面的内容加入到你的dependencyManagement部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.0.BUILD-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

Now this application is probably broken because there is no implementation of the JCL API on the classpath, so to fix it a new one has to be provided. In the next section we show you how to provide an alternative implementation of JCL using SLF4J as an example.

现在这个应用可能是坏了的,因为在classpath中没有JCL API的实现,为了解决这个问题必须提供一个新的实现。在接下来的部分我们将向你展示怎样提供一个JCL替代实现,使用SLF4J就是一个例子。

Using SLF4J

SLF4J is a cleaner dependency and more efficient at runtime than commons-logging because it uses compile-time bindings instead of runtime discovery of the other logging frameworks it integrates. This also means that you have to be more explicit about what you want to happen at runtime, and declare it or configure it accordingly. SLF4J provides bindings to many common logging frameworks, so you can usually choose one that you already use, and bind to that for configuration and management.

SLF4J是一个更纯净的依赖并且在运行时比commons-logging更有效,因为它使用编译时绑定来代替运行时查找集成的其它日志框架。这也意味着你必须更清楚你想要运行时发生什么,然后相应的声明它或配置它。SLF4J提供跟许多常用日志框架的绑定,因此你通常可以选择一个你正在使用的日志框架,然后绑定到配置和管理上。

SLF4J provides bindings to many common logging frameworks, including JCL, and it also does the reverse: bridges between other logging frameworks and itself. So to use SLF4J with Spring you need to replace the commons-logging dependency with the SLF4J-JCL bridge. Once you have done that then logging calls from within Spring will be translated into logging calls to the SLF4J API, so if other libraries in your application use that API, then you have a single place to configure and manage logging.

SLF4J提供跟许多常用日志框架的绑定,包括JCL,它做的恰恰相反,建立其它日志框架和它自己的纽带。因此为了在Spring中使用SLF4J,你需要用SLF4J-JCL连接器取替换commons-logging依赖。一旦你在Spring内部使用了日志调用,Spring会将日志调用变为调用SLF4J API,如果你应用中其它的库调用了那个API,你将有一个单独的地方配置和管理日志。

A common choice might be to bridge Spring to SLF4J, and then provide explicit binding from SLF4J to Log4J. You need to supply 4 dependencies (and exclude the existing commons-logging): the bridge, the SLF4J API, the binding to Log4J, and the Log4J implementation itself. In Maven you would do that like this

一个常用的选择连接Spring和SLF4J,然后提供SLF4J到Log4J的显式绑定。你需要提供四个依赖(排除现有的commons-logging):连接、SLF4J API、到Log4J的绑定、Log4J本身的实现。在Maven中你可能这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.0.BUILD-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>

That might seem like a lot of dependencies just to get some logging. Well it is, but it is optional, and it should behave better than the vanilla commons-logging with respect to classloader issues, notably if you are in a strict container like an OSGi platform. Allegedly there is also a performance benefit because the bindings are at compile-time not runtime.

这可能看起来为了得到一些日志需要很多依赖。还好它是可选的,比起commons-logging的关于类加载器的问题,尤其是你在一个像OSGi平台那样严格的容器中的时候,它应该更好操作。据说这儿也有一个性能提升,因为绑定是在编译时而不是在运行时。

A more common choice amongst SLF4J users, which uses fewer steps and generates fewer dependencies, is to bind directly to Logback. This removes the extra binding step because Logback implements SLF4J directly, so you only need to depend on two libraries not four ( jcl-over-slf4j and logback). If you do that you might also need to exclude the slf4j-api dependency from other external dependencies (not Spring), because you only want one version of that API on the classpath.

在SLF4J用户中,一个更通用的选择是直接绑定到Logback,这样使用步骤更少且依赖也更少。这去除了外部绑定步骤,因为Logback直接实现了SLF4J,因此你仅需要依赖两个库而不是四个(jcl-over-slf4jlogback)。如果你这样做的话你可能也需要从其它的外部应用中(不是从Spring)排除slf4j-api依赖,因为你在classpath中仅需要一个版本的API。

Using Log4J

Many people use Log4j as a logging framework for configuration and management purposes. It’s efficient and well-established, and in fact it’s what we use at runtime when we build and test Spring. Spring also provides some utilities for configuring and initializing Log4j, so it has an optional compile-time dependency on Log4j in some modules.

许多人使用Log4j作为配置和管理的日志框架。它有效且完善的,当我们构建和测试Spring时,实际上这就是在运行时我们使用的东西。Spring也提供一些配置和初始化Log4j的工具,因此在某些模块有可选的Log4j的编译时依赖。

To make Log4j work with the default JCL dependency (commons-logging) all you need to do is put Log4j on the classpath, and provide it with a configuration file ( log4j.properties or log4j.xml in the root of the classpath). So for Maven users this is your dependency declaration:

为了使Log4j能与默认的JCL依赖(commons-logging)一起工作,所有你需要做的是把Log4j放到classpath中,并提供一个配置文件(log4j.propertieslog4j.xml在classpath的根目录)。对于Maven用户依赖声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>

And here’s a sample log4j.properties for logging to the console:

下面是一个log4j.properties输出日志到控制台的样本:

image

Runtime Containers with Native JCL

Many people run their Spring applications in a container that itself provides an implementation of JCL. IBM Websphere Application Server (WAS) is the archetype. This often causes problems, and unfortunately there is no silver bullet solution; simply excluding commons-logging from your application is not enough in most situations.

许多人在容器中运行他们的Spring应用,容器本身提供了一个JCL实现。IBM Websphere Application Server (WAS) 是原型。这经常会引起问题,不幸的是没有一劳永逸的解决方案;在大多数环境下简单的执行commons-logging是不够的。

To be clear about this: the problems reported are usually not with JCL per se, or even with commons-logging: rather they are to do with binding commons-logging to another framework (often Log4J). This can fail because commons-logging changed the way they do the runtime discovery in between the older versions (1.0) found in some containers and the modern versions that most people use now (1.1). Spring does not use any unusual parts of the JCL API, so nothing breaks there, but as soon as Spring or your application tries to do any logging you can find that the bindings to Log4J are not working.

为了使这个更清楚:报告的问题本质上一般不是关于JCL的,或关于commons-logging的:而是他们去绑定commons-logging到其它的框架上(通常是Log4j)。这可能会失败因为commons-logging在一些容器的旧版本(1.0)和大多数人使用的现代版本(1.1)中改变了运行时发现方式。Spring不使用JCL API的和任何不常用的部分,因此不会有问题出现,但是一旦Spring或你的应用试图去输出日志,你可能发现到Log4j的绑定是不起作用的。

In such cases with WAS the easiest thing to do is to invert the class loader hierarchy (IBM calls it “parent last”) so that the application controls the JCL dependency, not the container. That option isn’t always open, but there are plenty of other suggestions in the public domain for alternative approaches, and your mileage may vary depending on the exact version and feature set of the container.

在这种情况下使用WAS最容易做的事是逆转类加载层(IBM称为”parent last”),为的是应用能控制依赖,而不是容器。虽然这种选择并非总是公开的,但在公共领域对于替代方法有许多其它的建议,你的解决这个问题花的时间可能是不同的,这取决于确定的版本和容器集合的特性。

Part II. Core Technologies

This part of the reference documentation covers all of those technologies that are absolutely integral to the Spring Framework.

这部分参考文档包含了所有完全集成到Spring框架中的那些技术。

Foremost amongst these is the Spring Framework’s Inversion of Control (IoC) container. A thorough treatment of the Spring Framework’s IoC container is closely followed by comprehensive coverage of Spring’s Aspect-Oriented Programming (AOP) technologies. The Spring Framework has its own AOP framework, which is conceptually easy to understand, and which successfully addresses the 80% sweet spot of AOP requirements in Java enterprise programming.

在这些中最重要的是Spring框架的控制反转(IoC)容器。对Spring框架IoC容器的彻底处理是紧随其后的Spring面向切面编程(AOP)技术的全面覆盖。Spring框架有它自己的AOP框架,这在概念上很容易理解,在Java企业级开发中成功了解决了AOP需求中80%的关键点。

Coverage of Spring’s integration with AspectJ (currently the richest - in terms of features - and certainly most mature AOP implementation in the Java enterprise space) is also provided.

Spring也提供了AspectJ的全面集成(目前是最丰富的-考虑到功能-并且确定在Java企业中是最成熟的AOP实现)。

  • Chapter 3, The IoC container
  • Chapter 4, Resources
  • Chapter 5, Validation, Data Binding, and Type Conversion
  • Chapter 6, Spring Expression Language (SpEL)
  • Chapter 7, Aspect Oriented Programming with Spring
  • Chapter 8, Spring AOP APIs

3. The IoC container

3.1 Introduction to the Spring IoC container and beans

This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.

这一章包含了Spring框架的控制反转(IoC)原理的实现。IoC也被称为依赖注入(DI)。它是一个处理过程,凭借对象之间依赖关系,也就是和它们一起工作的其它对象,只能通过构造函数参数,传递参数给工厂方法,在构造完成或工厂方法返回的对象实例之后再设置对象实例的属性。当创建bean时容器再将这些依赖对象注入进去。这个过程从根本上颠倒了bean本身通过直接构建类或通过一种机制例如服务定位模式来控制依赖对象的实例化或定位,因此命名为控制反转(IoC)。

The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory. It adds easier integration with Spring’s AOP features; message resource handling (for use in internationalization), event publication; and application-layer specific contexts such as the WebApplicationContext for use in web applications.

org.springframework.beansorg.springframework.context包是Spring框架控制反转容器的基础。BeanFactory接口提供了一种能管理任何类型对象的高级配置机制。ApplicationContextBeanFactory的一个子接口。ApplicationContext增加了更容易集成Spring AOP功能;消息资源处理(用在国际化中),事件发布;应用层特定上下文例如WebApplicationContext在web应用中的使用。

In short, the BeanFactory provides the configuration framework and basic functionality, and the ApplicationContext adds more enterprise-specific functionality. The ApplicationContext is a complete superset of the BeanFactory, and is used exclusively in this chapter in descriptions of Spring’s IoC container. For more information on using the BeanFactory instead of the ApplicationContext, refer to Section 3.16, “The BeanFactory”.

总之,BeanFactory提供了配置框架和基本功能,ApplicationContext增加了更多企业专用的功能。ApplicationContextBeanFactory的一个全面超集,在这章仅仅是用来描述Spring的IoC容器。关于用BeanFactory代替ApplicationContext的更多信息请参考3.16小节”The BeanFactory”。

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

在Spring中,被Spring IoC容器管理的那些形成你应用主干的对象被称为beans。bean是实例化、组装、以及其它的都被Spring IoC容器管理的对象。另外,bean仅仅是你应用中许多对象中的一个。Beans和它们之间的依赖关系,通过容器使用的配置元数据可以反映出来。

3.2 Container overview

The interface org.springframework.context.ApplicationContext represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the aforementioned beans. The container gets its instructions on what objects to instantiate, configure, and assemble by reading configuration metadata. The configuration metadata is represented in XML, Java annotations, or Java code. It allows you to express the objects that compose your application and the rich interdependencies between such objects.

org.springframework.context.ApplicationContext接口代表了Spring IoC容器并且负责实例化、配置和组装前面提到的beans。容器通过读取配置元数据得到说明什么对象要实例化、配置和组装。配置元数据可以用XML、Java注解或Java代码表示。它允许你表示构成应用的对象和对象间丰富的依赖关系。

Several implementations of the ApplicationContext interface are supplied out-of-the-box with Spring. In standalone applications it is common to create an instance of ClassPathXmlApplicationContext or FileSystemXmlApplicationContext. While XML has been the traditional format for defining configuration metadata you can instruct the container to use Java annotations or code as the metadata format by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats.

Spring提供了一些可以直接使用的ApplicationContext接口实现。在单独的应用中通常是创建一个 ClassPathXmlApplicationContext实例或FileSystemXmlApplicationContext实例。虽然XML是定义配置元数据的传统格式,但你可以指示容器支持使用Java注解或代码作为元数据的格式并通过提供少量的XML配置声明使容器支持这些额外的元数据格式。

In most application scenarios, explicit user code is not required to instantiate one or more instances of a Spring IoC container. For example, in a web application scenario, a simple eight (or so) lines of boilerplate web descriptor XML in the web.xml file of the application will typically suffice (see Section 3.15.4, “Convenient ApplicationContext instantiation for web applications”). If you are using the Spring Tool Suite Eclipse-powered development environment this boilerplate configuration can be easily created with few mouse clicks or keystrokes.

在大多数应用场景中,不会要求用户用显式的代码来实例化一个或多个Spring IoC容器的。例如,在web应用场景中,在应用的web.xml文件中写一个简单的八行左右的样板web描述符XML就足够了(看3.13.4小节,『web应用中ApplicationContext的方便实例化』)。如果你正在使用Eclipse支持的Spring Tool Suite开发环境,可以很容易的通过点几下鼠标或键盘来创建样本配置。

The following diagram is a high-level view of how Spring works. Your application classes are combined with configuration metadata so that after the ApplicationContext is created and initialized, you have a fully configured and executable system or application.

下面的图是从一个高层次的视野来看Spring是如何工作的。你的应用类与配置元数据结合起来为的是在ApplicationContext创建和初始化之后,你有一个完整配置并可执行的系统或应用。

Figure 3.1. The Spring IoC container
image

3.2.1 Configuration metadata

As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata; this configuration metadata represents how you as an application developer tell the Spring container to instantiate, configure, and assemble the objects in your application.

如上图所示,Spring IoC容器使用了一种配置元数据的方式;配置元数据表示你作为一个应用开发者应该告诉Spring容器怎样去实例化、配置并组装应用中的对象。

Configuration metadata is traditionally supplied in a simple and intuitive XML format, which is what most of this chapter uses to convey key concepts and features of the Spring IoC container.

习惯上用简单直观下XML形式来提供配置元数据,这一章大部分使用XML文件来表达Spring IoC容器的核心概念及功能。

XML-based metadata is not the only allowed form of configuration metadata. The Spring IoC container itself is totally decoupled from the format in which this configuration metadata is actually written. These days many developers choose Java-based configuration for their Spring applications.

 

基于XML的元数据不是配置元数据的唯一许可形式。Spring IoC容器本身与配置元数据的实际书写形式是完全解构的。目前许多开发者在他们的Spring应用中选用基于Java配置的元数据形式。

 
For information about using other forms of metadata with the Spring container, see:

  • Annotation-based configuration: Spring 2.5 introduced support for annotation-based configuration metadata.
  • Java-based configuration: Starting with Spring 3.0, many features provided by the Spring JavaConfig project became part of the core Spring Framework. Thus you can define beans external to your application classes by using Java rather than XML files. To use these new features, see the @Configuration, @Bean, @Import and @DependsOn annotations.

关于Spring容器中使用其它元数据形式的信息,请看:

  • 基于注解的配置:Spring 2.5引入对了基于注解的配置元数据的支持。
  • 基于Java的配置:从Spring 3.0开始,Spring JavaConfig工程提供的许多功能开始成为Spring框架核心中的一部分。因此你可以通过Java而不是XML文件来定义外部应用程序的beans。为了使用这些新功能,请看@Configuration, @Bean, @Import@DependsOn注解。

Spring configuration consists of at least one and typically more than one bean definition that the container must manage. XML-based configuration metadata shows these beans configured as <bean/> elements inside a top-level <beans/> element. Java configuration typically uses @Bean annotated methods within a @Configuration class.

Spring配置包括至少一个且通常不止一个容器必须管理的bean定义。基于XML的配置元数据中,这些beans作为<bean>元素被配置在顶层<beans/>元素中。Java配置通常在@Configuration类中使用@Bean注解的方法。

These bean definitions correspond to the actual objects that make up your application. Typically you define service layer objects, data access objects (DAOs), presentation objects such as Struts Action instances, infrastructure objects such as Hibernate SessionFactories, JMS Queues, and so forth. Typically one does not configure fine-grained domain objects in the container, because it is usually the responsibility of DAOs and business logic to create and load domain objects. However, you can use Spring’s integration with AspectJ to configure objects that have been created outside the control of an IoC container. See Using AspectJ to dependency-inject domain objects with Spring.

这些bean定义与组成你应用的实际对象相对应。通常你会定义服务层对象,数据访问层对象(DAOs),描述对象例如Struts的Action实例,底层对象例如Hibernate的SessionFactories,JMS的Queues等等。容器中细粒度的领域对象通常是不配置的,因为一般是由DAOs和业务逻辑负责创建和加载领域对象。然而你可以使用Spring集成的AspectJ去配置IoC容器控制之外创建的对象。请看”使用Spring的AspectJ来依赖注入领域对象”。

The following example shows the basic structure of XML-based configuration metadata:

下面的例子展示了基于XML配置元数据的基本结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>

The id attribute is a string that you use to identify the individual bean definition. The class attribute defines the type of the bean and uses the fully qualified classname. The value of the id attribute refers to collaborating objects. The XML for referring to collaborating objects is not shown in this example; see Dependencies for more information.

id属性是一个你用来识别私有bean定义的字符串。class属性定义了bean的类型并且使用了完全限定类型名称(全限定名称或完全限定名)。id属性的值指的是协作对象。这个例子的中没有展示如何引用协作对象,更多信息请查看『依赖』。

3.2.2 Instantiating a container

Instantiating a Spring IoC container is straightforward. The location path or paths supplied to an ApplicationContext constructor are actually resource strings that allow the container to load configuration metadata from a variety of external resources such as the local file system, from the Java CLASSPATH, and so on.

实例化一个Spring IoC容器是简单的。一个或多个提供给ApplicationContext构造函数的定位路径实际上是资源字符串,可以让容器从各种例如局部文件系统,Java的CLASSPATH等外部资源中加载配置元数据。

1
2
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

After you learn about Spring’s IoC container, you may want to know more about Spring’s Resource abstraction, as described in Chapter 4, Resources, which provides a convenient mechanism for reading an InputStream from locations defined in a URI syntax. In particular, Resource paths are used to construct applications contexts as described in Section 4.7, “Application contexts and Resource paths”.

 

在你学习Spring IoC容器之后,你可能想知道更多关于Spring的Resource抽象信息,介绍信息在『第四章 资源』中,Resource抽象提供了一种方便的机制从URI语法定义的位置中读取输入流。Resource路径通常被用来构建应用程序上下文,正如4.7 小节『应用上下文和资源路径』描述的那样。

 
The following example shows the service layer objects (services.xml) configuration file:

下面的例子是服务层对象(services.xml)的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- services -->

<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions for services go here -->

</beans>

The following example shows the data access objects daos.xml file:

下面的例子是数据访问对象daos.xml的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>

<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions for data access objects go here -->

</beans>

In the preceding example, the service layer consists of the class PetStoreServiceImpl, and two data access objects of the type JpaAccountDao and JpaItemDao (based on the JPA Object/Relational mapping standard). The property name element refers to the name of the JavaBean property, and the ref element refers to the name of another bean definition. This linkage between id and ref elements expresses the dependency between collaborating objects. For details of configuring an object’s dependencies, see Dependencies.

在之前的例子中,服务层包括类PetStoreServiceImpl和两个类型为JpaAccountDaoJpaItemDao数据访问对象(基于JPA对象/关系映射标准)。property name元素指的是JavaBean属性的名称,ref元素指的是另一个bean定义的名称。idref之间的连接表明了协作对象之间的关系。配置对象依赖的更详细信息请看『依赖』。

Composing XML-based configuration metadata

It can be useful to have bean definitions span multiple XML files. Often each individual XML configuration file represents a logical layer or module in your architecture.

bean定义跨越多个XML文件是非常有用的。通常每一个独立的XML配置文件表示你架构中的一个逻辑层或模块。

You can use the application context constructor to load bean definitions from all these XML fragments. This constructor takes multiple Resource locations, as was shown in the previous section. Alternatively, use one or more occurrences of the <import/> element to load bean definitions from another file or files. For example:

你可以使用应用上下文构造函数从所有XML片段中加载bean定义。如上小节所示,构造函数可以接收多个Resource位置。也可以使用一个或同时使用多个<import/>元素从另一个或另一些文件中加载bean定义。例如:

1
2
3
4
5
6
7
8
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>

<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>

In the preceding example, external bean definitions are loaded from three files: services.xml, messageSource.xml, and themeSource.xml. All location paths are relative to the definition file doing the importing, so services.xml must be in the same directory or classpath location as the file doing the importing, while messageSource.xmland themeSource.xml must be in a resources location below the location of the importing file. As you can see, a leading slash is ignored, but given that these paths are relative, it is better form not to use the slash at all. The contents of the files being imported, including the top level <beans/> element, must be valid XML bean definitions according to the Spring Schema.

在上面的例子中,外部bean定义从services.xmlmessageSource.xmlthemeSource.xml三个文件中加载。所有位置路径都是相对于进行导入的定义文件的,因此services.xml必须跟进行导入的文件在同一个目录下或同一个classpath位置下。如你所见,忽略了最前面的反斜杠,但给定的这些路径是相对的,最好是一点都不使用反斜杠。包括顶层的<beans/>元素在内,被导入的文件内容必须是依据Spring Schema有效的XML bean定义。

It is possible, but not recommended, to reference files in parent directories using a relative “../“ path. Doing so creates a dependency on a file that is outside the current application. In particular, this reference is not recommended for “classpath:” URLs (for example, “classpath:../services.xml”), where the runtime resolution process chooses the “nearest” classpath root and then looks into its parent directory. Classpath configuration changes may lead to the choice of a different, incorrect directory.

You can always use fully qualified resource locations instead of relative paths: for example, “file:C:/config/services.xml” or “classpath:/config/services.xml”. However, be aware that you are coupling your application’s configuration to specific absolute locations. It is generally preferable to keep an indirection for such absolute locations, for example, through “${…​}” placeholders that are resolved against JVM system properties at runtime.

 

在父目录的引用文件使用”../“相对路径是可以的,但不推荐这样做。这样做会产生一个当前应用之外文件依赖。引用文件特别不推荐在”classpath:” URLs中(例如”classpath:../services.xml”),运行时解析处理会选择”最近的”classpath根目录,然后去寻找它的父目录。Classpath配置的更改可能会导致进入一个不同且不正确的目录。

你可以总是使用完全限定资源位置代替相对路径:例如,”file:C:/config/services.xml”或”classpath:/config/services.xml”。但是要注意你正在将你的应用配置与特定的绝对路径耦合。通常更可取的方式是间接的访问绝对路径,例如,通过”${…​}”占位符在运行时解析JVM系统属性。

 

3.2.3 Using the container

The ApplicationContext is the interface for an advanced factory capable of maintaining a registry of different beans and their dependencies. Using the method T getBean(String name, Class<T> requiredType) you can retrieve instances of your beans.

ApplicationContext是一个更高级的工厂接口,它能维护不同beans及其依赖的注册表。使用方法T getBean(String name, Class<T> requiredType)你可以取回你的beans实例。

The ApplicationContext enables you to read bean definitions and access them as follows:

ApplicationContext能让你用下面的方式读取bean定义及访问它们:

1
2
3
4
5
6
7
8
9
// create and configure beans
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

You use getBean() to retrieve instances of your beans. The ApplicationContext interface has a few other methods for retrieving beans, but ideally your application code should never use them. Indeed, your application code should have no calls to the getBean() method at all, and thus no dependency on Spring APIs at all. For example, Spring’s integration with web frameworks provides for dependency injection for various web framework classes such as controllers and JSF-managed beans.

你可以用getBean()取回你的beans实例。ApplicationContext接口有一些其它的方法来取回beans,但理想的应用代码应该绝不使用它们。事实上,你的应用代码应该完全不调用getBean()方法,因此完全不依赖Spring APIs。例如,Spring的集成web框架提供了各种web框架类的依赖注入,例如控制器和JSF管理的beans。

3.3 Bean overview

A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container, for example, in the form of XML <bean/> definitions.

Spring IoC容器管理一个或多个beans。这些beans由提供给容器的配置元数据生成,例如,XML形式的<bean/>定义。

Within the container itself, these bean definitions are represented as BeanDefinition objects, which contain (among other information) the following metadata:

  • A package-qualified class name: typically the actual implementation class of the bean being defined.
  • Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
  • References to other beans that are needed for the bean to do its work; these references are also called collaborators or dependencies.
  • Other configuration settings to set in the newly created object, for example, the number of connections to use in a bean that manages a connection pool, or the size limit of the pool.

在容器本身内部,这些bean定义被表示成BeanDefinition对象,含有以下元数据:

  • 包限定的类名:通常是被定义的bean的实现类。
  • bean行为配置元素,规定了bean在容器中的行为(作用范围、生命周期回调函数)等等。
  • bean工作需要的引用的其它bean,这些引用也被称为协作者或依赖。
  • 其它的配置在新创建的对象中设置,例如,bean中使用的连接数量控制着一个连接池,或连接池的大小限制。

This metadata translates to a set of properties that make up each bean definition.

元数据转化为一系列的属性,这些属性构成了每个bean的定义。

Table 3.1. The bean definition

Property Explained in…
class Section 3.3.2, “Instantiating beans”
name Section 3.3.1, “Naming beans”
scope Section 3.5, “Bean scopes”
constructor arguments Section 3.4.1, “Dependency Injection”
properties Section 3.4.1, “Dependency Injection”
autowiring mode Section 3.4.5, “Autowiring collaborators”
lazy-initialization mode Section 3.4.4, “Lazy-initialized beans”
initialization method the section called “Initialization callbacks”
destruction method the section called “Destruction callbacks”

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext implementations also permit the registration of existing objects that are created outside the container, by users. This is done by accessing the ApplicationContext’s BeanFactory via the method getBeanFactory() which returns the BeanFactory implementation DefaultListableBeanFactory. DefaultListableBeanFactory supports this registration through the methods registerSingleton(..) and registerBeanDefinition(..). However, typical applications work solely with beans defined through metadata bean definitions.

除了bean定义中包含怎么创建一个指定的bean的信息之外,ApplicationContext实现也允许用户注册容器之外创建的现有对象。这是通过调用ApplicationContext’s BeanFactory的getBeanFactory()方法完成的,这个方法会返回BeanFactory的实现类DefaultListableBeanFactoryDefaultListableBeanFactory支持通过registerSingleton(..)registerBeanDefinition(..)方法来注册。不管怎样,标准应用仅使用通过元数据bean定义定义的beans。

Bean metadata and manually supplied singleton instances need to be registered as early as possible, in order for the container to properly reason about them during autowiring and other introspection steps. While overriding of existing metadata and existing singleton instances is supported to some degree, the registration of new beans at runtime (concurrently with live access to factory) is not officially supported and may lead to concurrent access exceptions and/or inconsistent state in the bean container.

 

bean元数据和人工提供的单例需要尽可能早的进行注册,为了使容器在自动注入及其它的内省步骤时能恰当的推理它们。虽然在一定程度上是支持覆盖现有的元数据和单例的,但运行时新beans的注册(并发实时访问工厂)是不被正式支持的,可能会引起并发访问异常,在容器中的与/或状态不一致。

 

3.3.1 Naming beans

Every bean has one or more identifiers. These identifiers must be unique within the container that hosts the bean. A bean usually has only one identifier, but if it requires more than one, the extra ones can be considered aliases.

每个bean都有一个或多个标识符。这些托管bean的标识符在容器中必须是唯一的。一个bean通常只有一个标识符,但如果一个bean需要不止一个标识符,其它的标识符会被当成别名。

In XML-based configuration metadata, you use the id and/or name attributes to specify the bean identifier(s). The id attribute allows you to specify exactly one id. Conventionally these names are alphanumeric (‘myBean’, ‘fooService’, etc.), but may contain special characters as well. If you want to introduce other aliases to the bean, you can also specify them in the name attribute, separated by a comma (,), semicolon (;), or white space. As a historical note, in versions prior to Spring 3.1, the id attribute was defined as an xsd:ID type, which constrained possible characters. As of 3.1, it is defined as an xsd:string type. Note that bean id uniqueness is still enforced by the container, though no longer by XML parsers.

在基于XML的配置元数据中,你可以使用id和/或name属性指定bean标识符。id属性允许你指定一个确定的id。按照惯例这些名字是字母数字的(‘myBean’, ‘fooService’等等),但也可能包含指定字符。如果你想引入bean其它的别名,你可以在name属性中指定别名,用逗号 (,),分号(;),或空格分开。作为一个历史注解,在之前的Spring 3.1版本,id属性被定义为一种xsd:ID类型,可以通过合理字符来约束(XML控制id唯一性)。从Spring 3.1开始,它被定义为xsd:string类型。注意bean id的唯一性仍然是容器强制的,虽然不再通过XML解析器来控制(容器控制id唯一性)。

You are not required to supply a name or id for a bean. If no name or id is supplied explicitly, the container generates a unique name for that bean. However, if you want to refer to that bean by name, through the use of the ref element or Service Locator style lookup, you must provide a name. Motivations for not supplying a name are related to using inner beans and autowiring collaborators.

bean的id和name不是必须提供的。如果没有明确指定name或id,容器会为bean产生一个唯一的名字。如果你想通过name引用bean,通过使用ref元素或服务定位器模式查找,你必须提供一个名字。不提供name的动机是与内部bean的使用和协作bean的自动装配有关的。

Bean Naming Conventions

The convention is to use the standard Java convention for instance field names when naming beans. That is, bean names start with a lowercase letter, and are camel-cased from then on. Examples of such names would be (without quotes) ‘accountManager’, ‘accountService’, ‘userDao’, ‘loginController’, and so forth.

Naming beans consistently makes your configuration easier to read and understand, and if you are using Spring AOP it helps a lot when applying advice to a set of beans related by name.

 

Bean命名规范

当命名bean时,采用的规范是标准Java实例字段命名规范。bean名称以小写字母开头,采用驼峰式的命名规则。这种命名方式的例子(不带引号)有’accountManager’, ‘accountService’, ‘userDao’, ‘loginController’等等。

一致的命名beans可以使人更容易读懂和理解你的配置,如果你正在使用Spring AOP,使用一致性来命名一系列bean名称是非常有帮助的。

 

With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules above: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by java.beans.Introspector.decapitalize (which Spring is using here).

 

在classpath中进行组件扫描,Spring会根据上面的规则为未命名组件产生bean名称,本质上来说,是采用简单的类名并将其首字母改成小写。然而在特殊情况下(不平常的),当类名有不止一个字母且第一二个字母都是大写的情况下,会保留最初始的状态。与java.beans.Introspector.decapitalize定义中的规则是相同的(Spring也采用这个规则)。

Aliasing a bean outside the bean definition

In a bean definition itself, you can supply more than one name for the bean, by using a combination of up to one name specified by the id attribute, and any number of other names in the name attribute. These names can be equivalent aliases to the same bean, and are useful for some situations, such as allowing each component in an application to refer to a common dependency by using a bean name that is specific to that component itself.

在定义bean时,通过与id属性指定的名称相结合,你可以为bean提供不止一个名字,在name属性中定义任何数量的其它名字。这些名字是同一个bean的等价别名,在一些情况下是非常有用的,例如允许应用中的每个组件通过bean名称引用一个共通的依赖,这个依赖为每个组件本身指定了一个名称。

Specifying all aliases where the bean is actually defined is not always adequate, however. It is sometimes desirable to introduce an alias for a bean that is defined elsewhere. This is commonly the case in large systems where configuration is split amongst each subsystem, each subsystem having its own set of object definitions. In XML-based configuration metadata, you can use the <alias/> element to accomplish this.

然而在bean实际定义的地方指定所有别名并不总是适当的。有时会要求引入一个在别的地方定义的bean的别名。这通常是在大的系统中而配置被分割在每个子系统中,每个子系统有它知道对象定义集合。在基于XML配置元数据中,你可以使用<alias/>来完成别名的定义。

1
<alias name="fromName" alias="toName"/>

In this case, a bean in the same container which is named fromName, may also, after the use of this alias definition, be referred to as toName.

在这种情况下,在同一个容器中的bean被命名为fromName,也可能是在别名定义使用之后,被作为toName引用。

For example, the configuration metadata for subsystem A may refer to a DataSource via the name subsystemA-dataSource. The configuration metadata for subsystem B may refer to a DataSource via the name subsystemB-dataSource. When composing the main application that uses both these subsystems the main application refers to the DataSource via the name myApp-dataSource. To have all three names refer to the same object you add to the MyApp configuration metadata the following aliases definitions:

例如,子系统A的配置元数据可能通过名称subsystemA-dataSource引用数据源。子系统B的配置元数据可能通过名称subsystemB-dataSource引用数据源。当构成主应用的时,主应用使用这些子系统并通过名称myApp-dataSource引用数据源。为了使这三个名称引用同一个对象,你可以将如下的别名定义添加到MyApp配置元数据中:

1
2
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />

Now each component and the main application can refer to the dataSource through a name that is unique and guaranteed not to clash with any other definition (effectively creating a namespace), yet they refer to the same bean.

现在主应用和每个组件都能通过名称引用数据源,这个名称是唯一的且能保证不与任何其它的定义相冲突(有效的创建了一个命名空间),但它们引用了同一个bean。

Java-configuration

If you are using Java-configuration, the @Bean annotation can be used to provide aliases see Section 3.12.3, “Using the @Bean annotation” for details.

 

Java配置

如果你正在使用Java配置,@Bean注解可以用来提供别名,更多细节请看3.12.3小节, “使用@Bean注解”。

3.3.2 Instantiating beans

A bean definition essentially is a recipe for creating one or more objects. The container looks at the recipe for a named bean when asked, and uses the configuration metadata encapsulated by that bean definition to create (or acquire) an actual object.

bean定义本质上来说是创建一个或多个对象的方法。当问及一个命名bean时,容器会查看这个方法并使用bean定义中封装的配置元数据创建(或取得)一个实际的对象。

If you use XML-based configuration metadata, you specify the type (or class) of object that is to be instantiated in the class attribute of the <bean/> element. This class attribute, which internally is a Class property on a BeanDefinition instance, is usually mandatory. (For exceptions, see the section called “Instantiation using an instance factory method” and Section 3.7, “Bean definition inheritance”.) You use the Class property in one of two ways:

  • Typically, to specify the bean class to be constructed in the case where the container itself directly creates the bean by calling its constructor reflectively, somewhat equivalent to Java code using the new operator.

  • To specify the actual class containing the static factory method that will be invoked to create the object, in the less common case where the container invokes a static factory method on a class to create the bean. The object type returned from the invocation of the static factory method may be the same class or another class entirely.

如果你使用基于XML的配置元数据,你可以指定对象的类型(或类),它将在<bean/>元素中的class属性中进行实例化。class属性,在BeanDefinition实例的内部是Class性质,通常是必需的。(例外的情况,请看”使用实例工厂方法进行实例化”小节和3.7小节,”bean定义继承”)。你可以通过以下两种方式中的一种使用Class属性:

  • 通常情况下,指定要构造的bean类,容器本身通过反射调用bean的构造方法直接创建bean,这与Java代码中使用new操作符是等价的。

  • 在不常见的情况下,指定包含静态工厂方法的实际类,调用静态工厂方法创建对象,容器在类上调用静态工厂方法创建bean。静态工厂方法调用返回的对象类型可能是同一个类,也可能完全是另一个类。

Inner class names. If you want to configure a bean definition for a static nested class, you have to use the binary name of the nested class.

For example, if you have a class called Foo in the com.example package, and this Foo class has a static nested class called Bar, the value of the 'class' attribute on a bean definition would be…​

com.example.Foo$Bar

Notice the use of the $ character in the name to separate the nested class name from the outer class name.

 

内部类命名 如果你想为静态嵌套类配置bean定义,你必须使用嵌套类的二进制名字。

例如,如果你在com.example包中有个类叫FooFoo类中有一个静态嵌套类叫Bar'class'属性在bean定义中的值为

com.example.Foo$Bar

注意名字中$符号的使用是为了将外部类名与嵌套类名分隔开。

 
Instantiation with a constructor

When you create a bean by the constructor approach, all normal classes are usable by and compatible with Spring. That is, the class being developed does not need to implement any specific interfaces or to be coded in a specific fashion. Simply specifying the bean class should suffice. However, depending on what type of IoC you use for that specific bean, you may need a default (empty) constructor.

当你使用构造方法创建bean时,所有的正常类都可以被Spring使用和兼容。也就是说,正在进行开发的类不需要实现任何特定的接口或以特定的方式进行编码。简单的指定bean类就足够了。然而,根据你为指定的bean所使用的IoC类型,你可能需要一个默认的(空的)构造函数。

The Spring IoC container can manage virtually any class you want it to manage; it is not limited to managing true JavaBeans. Most Spring users prefer actual JavaBeans with only a default (no-argument) constructor and appropriate setters and getters modeled after the properties in the container. You can also have more exotic non-bean-style classes in your container. If, for example, you need to use a legacy connection pool that absolutely does not adhere to the JavaBean specification, Spring can manage it as well.

事实上,Spring的IoC容器可以管理任何你想让它管理的类;它不受限于管理真实的JavaBeans。大多数Spring用户更喜欢实际的JavaBeans,在容器中它仅有一个默认(无参)的构造函数,并且属性之后有合适的setters,getters方法。在容器中你也可以有更多外来的非bean类型的类。例如,如果你需要使用遗留的连接池,这绝对不符合JavaBean规范,但Spring也可以管理它。

With XML-based configuration metadata you can specify your bean class as follows:

基于XML的配置元数据你可以用如下方式指定你的bean的类:

1
2
3
<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

For details about the mechanism for supplying arguments to the constructor (if required) and setting object instance properties after the object is constructed, see Injecting Dependencies.

更多关于为构造函数提供参数(如果有必要的话)的机制和构造对象之后设置对象实例属性的细节,请看”依赖注入”。

Instantiation with a static factory method

When defining a bean that you create with a static factory method, you use the class attribute to specify the class containing the static factory method and an attribute named factory-method to specify the name of the factory method itself. You should be able to call this method (with optional arguments as described later) and return a live object, which subsequently is treated as if it had been created through a constructor. One use for such a bean definition is to call static factories in legacy code.

当定义的bean用静态工厂方法创建时,你可以使用class属性指定包含静态工厂方法的类,用factory-method属性指定工厂方法本身的名字。你应该能调用这个方法(用后面描述的可选参数)并且返回一个实时对象,随后对这个对象进行处理,就好像这个对象是通过构造函数创建的一样。这种bean定义的一个用法是在遗留代码(旧代码)中调用静态工厂方法。

The following bean definition specifies that the bean will be created by calling a factory-method. The definition does not specify the type (class) of the returned object, only the class containing the factory method. In this example, the createInstance() method must be a static method.

下面的bean定义指定了一个通过调用工厂方法创建的bean。定义没有指定返回对象的类型只有包含工厂方法的类。在这个例子中,createInstance()必须是一个静态方法。

1
2
3
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
1
2
3
4
5
6
7
8
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}

public static ClientService createInstance() {
return clientService;
}
}

For details about the mechanism for supplying (optional) arguments to the factory method and setting object instance properties after the object is returned from the factory, see Dependencies and configuration in detail.

更多关于为工厂方法提供(可选)参数的原理和从工厂方法返回对象后设置对象实例属性的信息,请看”依赖和详细配置”。

Instantiation using an instance factory method

Similar to instantiation through a static factory method, instantiation with an instance factory method invokes a non-static method of an existing bean from the container to create a new bean. To use this mechanism, leave the class attribute empty, and in the factory-bean attribute, specify the name of a bean in the current (or parent/ancestor) container that contains the instance method that is to be invoked to create the object. Set the name of the factory method itself with the factory-method attribute.

与通过静态工厂方法进行实例化类似,通过实例化工厂方法进行实例化,要从容器中调用现有bean非静态方法创建一个新的bean。使用这种机制,要让class属性为空,在factory-bean属性中,在包含实例化方法的当前容器(或父/祖先)中指定bean的名字,通过调用实例化方法来创建对象。通过factory-method属性设置工厂方法本身的名字。

1
2
3
4
5
6
7
8
9
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
1
2
3
4
5
6
7
8
9
public class DefaultServiceLocator {

private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}

public ClientService createClientServiceInstance() {
return clientService;
}
}

One factory class can also hold more than one factory method as shown here:

一个工厂类可以拥有多个工厂方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>

<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DefaultServiceLocator {

private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();

private DefaultServiceLocator() {}

public ClientService createClientServiceInstance() {
return clientService;
}

public AccountService createAccountServiceInstance() {
return accountService;
}

}

This approach shows that the factory bean itself can be managed and configured through dependency injection (DI). See Dependencies and configuration in detail.

这个方法展示了工厂bean本身可以通过依赖注入(DI)来管理和配置。更多细节请看”依赖和配置”。

In Spring documentation, factory bean refers to a bean that is configured in the Spring container that will create objects through an instance or static factory method. By contrast, FactoryBean (notice the capitalization) refers to a Spring-specific FactoryBean.

 

在Spring文档中,工厂bean引用了配置在Spring容器中的bean,Spring容器将通过实例或静态工厂方法来创建对象。相比之下,FactoryBean(注意大写)引用了Spring特定的FactoryBean

3.4 Dependencies

A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.

标准企业应用不会由一个对象(或Spring用语中的bean)组成。即使是最简单的应用也是由一些对象共同工作,呈现给终端用户用户看到的是一个连续的应用。接下来的一节阐述了如何从定义许多独立的bean定义到完全实现的应用,它是一个通过对象协作来实现目标的过程。

3.4.1 Dependency Injection

Dependency injection (DI) is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes, or the Service Locator pattern.

依赖注入(DI)是一个处理过程,凭借对象之间依赖关系,也就是和它们一起工作的其它对象,只能通过构造函数参数,传递参数给工厂方法,在构造完成或工厂方法返回对象实例之后再设置对象实例的属性。当创建bean时容器再将这些依赖对象注入进去。这个过程从根本上颠倒了bean本身通过直接构建类或通过一种机制例如服务定位模式来控制依赖对象的实例化或定位,因此命名为控制反转(IoC)

Code is cleaner with the DI principle and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies, and does not know the location or class of the dependencies. As such, your classes become easier to test, in particular when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

使用依赖注入原则会使代码更简洁,当对象由依赖关系提供时解耦更有效。对象不会查找它的依赖,不知道依赖的位置和依赖关系的类别。同样的,你的类也变的更容易测试,尤其是依赖关系在接口或抽象基类之间的时候,这种情况下单元测试中会要求存桩或模拟实现。(注:Stub和Mock都是软件测试中使用的东西,如有疑问请自行google或百度)。

DI exists in two major variants, Constructor-based dependency injection and Setter-based dependency injection.

依赖有两个主要变种,基于构造函数的依赖注入和基于Setter的依赖注入。

Constructor-based dependency injection

Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static factory method similarly. The following example shows a class that can only be dependency-injected with constructor injection. Notice that there is nothing special about this class, it is a POJO that has no dependencies on container specific interfaces, base classes or annotations.

基于构造函数的依赖注入通过容器调用有参数的构造函数来实现,每个参数表示一个依赖。调用指定参数的静态工厂方法来构造bean是近似等价的,这里的讨论将给构造函数和静态工厂方法传参看成是类似的。接下来的例子展示了一个类仅能通过构建函数注入进行依赖注入。注意这个类没什么特别的,它是一个POJO,不依赖于容器特定的接口,基类或注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SimpleMovieLister {

// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;

// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// business logic that actually uses the injected MovieFinder is omitted...

}

Constructor argument resolution

Constructor argument resolution matching occurs using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class:

构造函数参数解析使用参数类型进行匹配。如果bean定义的构造函数参数中不存在潜在的歧义,bean定义中定义构造函数参数的顺序为bean实例化时提供给恰当构造函数的参数顺序。细想下面的类:

1
2
3
4
5
6
7
8
9
package x.y;

public class Foo {

public Foo(Bar bar, Baz baz) {
// ...
}

}

No potential ambiguity exists, assuming that Bar and Baz classes are not related by inheritance. Thus the following configuration works fine, and you do not need to specify the constructor argument indexes and/or types explicitly in the <constructor-arg/> element.

不存在潜在的歧义,假设Bar类和Baz类之间不存在继承关系。因此下面的配置会工作良好,你不必在<constructor-arg/>元素中显式的指定构造函数参数索引的与/或类型。

1
2
3
4
5
6
7
8
9
10
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>

<bean id="bar" class="x.y.Bar"/>

<bean id="baz" class="x.y.Baz"/>
</beans>

When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as <value>true</value>, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:

当引用另一个bean时,类型已知,匹配正确(像上面的例子一样)。当使用简单类型时,例如<value>true</value>,Spring不能决定值的类型,因此没有帮助不能按类型匹配。考虑下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package examples;

public class ExampleBean {

// Number of years to calculate the Ultimate Answer
private int years;

// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;

public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}

}

In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument using the type attribute. For example:

在上面的场景中,如果你用type属性显式的指定了构造参数的类型,对于简单类型容器可以使用类型匹配。例如:

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>

Use the index attribute to specify explicitly the index of constructor arguments. For example:

使用index属性来显式的指定构造函数参数的索引,例如:

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>

In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has two arguments of the same type. Note that the index is 0 based.

除了要解析多个简单值的歧义性之外,当构造函数有两个相同类型的的参数时,指定索引可以解决歧义问题。注意索引是从0开始的。

You can also use the constructor parameter name for value disambiguation:

你也可以使用构造函数参数名字解决值的歧义问题。

1
2
3
4
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>

Keep in mind that to make this work out of the box your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you can’t compile your code with debug flag (or don’t want to) you can use @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:

记住,要使这个起作用你的代码必须使用调试模式进行编译,这样Spring可以从构造函数中查找参数名称。如果你不能用调试模式进行编译(或不想),你可以使用JDK注解@ConstructorProperties显式的命名你的构造函数参数。样板类如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
package examples;

public class ExampleBean {

// Fields omitted

@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}

}

Setter-based dependency injection

Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.

基于Setter的依赖注入在容器调用无参构造函数或无参静态工厂方法之后,通过调用bean的setter方法来实现依赖注入。

The following example shows a class that can only be dependency-injected using pure setter injection. This class is conventional Java. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.

下面的例子显示了一个类只能通过纯粹的setter注入进行依赖注入。这个类是常见的Java类。它是一个不依赖于容器中特定接口、基类或注解的POJO。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SimpleMovieLister {

// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;

// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// business logic that actually uses the injected MovieFinder is omitted...

}

The ApplicationContext supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (i.e., programmatically) but rather with XML bean definitions, annotated components (i.e., classes annotated with @Component, @Controller, etc.), or @Bean methods in Java-based @Configuration classes. These sources are then converted internally into instances of BeanDefinition and used to load an entire Spring IoC container instance.

ApplicationContext支持基于构造函数和基于setter对它管理的bean进行依赖注入。它也支持一些依赖通过构造函数方法注入之后,使用基于setter的依赖注入。使用BeanDefinition形式配置依赖项,结合PropertyEditor实例可以将属性从一种形式转成另一种形式。然而大多数Spring用户直接使用这些类(例如以编程形式),而使用XML定义bean,注解组件(例如类中使用 @Component,,@Controller注解等等),或在基于Java的@Configuration类使用@Bean方法。

Constructor-based or setter-based DI?

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required annotation on a setter method can be used to make the property a required dependency.

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.

Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.

 

使用基于构造函数的依赖注入还是基于setter的依赖注入?

你可以混合使用基于构造函数的依赖注入和基于setter的依赖注入,强制依赖使用构造函数注入,可选依赖使用setter方法或配置方法注入是一个很好的经验法则。注意在setter方法上使用@Required注解会检查依赖是否注入。

当实现的应用组件是不可变对象时,Spring团队通常主张构造函数注入,这样可以确保所需的依赖非空。此外,基于构造函数注入的组件总是以完全初始化状态返回客户(调用)代码。作为附注,含有许多构造函数参数的代码给人的感觉很差,这意味着类可能有很多职责,应该进行重构以便更好的处理关注问题的分离。

setter注入应该主要用来可选依赖上,在类内可以给可选依赖指定合理的默认值。此外,在每处使用依赖的代码都要进行非空检查。setter注入的一个好处就是setter方法使类的对象在后面可以进行再配置或再注入。JMX MBeans的管理是setter注入一个非常好的案例。

使用依赖注入的类型对于特定的类是最有意义的。有时候,当处理没有源码的第三方类时,使用哪种方式取决于你。例如,如果第三方库没有提供任何setter方法,构造函数注入可能是依赖注入唯一可行的方式。

Dependency resolution process

The container performs bean dependency resolution as follows:

  • The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified via XML, Java code, or annotations.

  • For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.

  • Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.

  • Each property or constructor argument which is a value is converted from its specified format to the actual type of that property or constructor argument. By default Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, etc.

容器按下面的过程处理bean依赖解析:

  • 创建ApplicationContext并使用描述所有bean的配置元数据初始化ApplicationContext,配置元数据可以通过XML,Java代码或注解指定。

  • 对于每一个bean,它的依赖通过属性、构造函数参数、或静态工厂方法参数的形式表示,静态工厂方法可以替代标准的构造函数。当bean在实际创建时,这些依赖会提供给bean。

  • 每个属性或构造函数参数或者是根据实际定义设置的值,或者是容器中另一个bean的引用。

  • 每个属性或构造函数参数是一个从指定形式转成实际类型的属性或构造函数参数的值。

The Spring container validates the configuration of each bean as the container is created. However, the bean properties themselves are not set until the bean is actually created. Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created. Scopes are defined in Section 3.5, “Bean scopes”. Otherwise, the bean is created only when it is requested. Creation of a bean potentially causes a graph of beans to be created, as the bean’s dependencies and its dependencies’ dependencies (and so on) are created and assigned. Note that resolution mismatches among those dependencies may show up late, i.e. on first creation of the affected bean.

当容器创建后Spring容器会验证每个bean的配置。然而,bean属性本身只有bean创建时才会进行设置。bean是单例的并且当容器创建时会进行提前实例化(默认情况)。作用范围是在3.5 小节”Bean scopes”中定义的。此外,只有需要时候才会创建bean。bean的创建可能会引起beans图的创建,当bean的依赖和它的依赖的依赖(等等)创建和指定的时候。注意这些依赖中解析不匹配可能会在后面出现,例如,受影响的bean第一次创建时。

Circular dependencies

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).

 

循环依赖

如果你主要使用构造函数注入,有可能会出现一个不能解决的循环依赖状况。

例如,类A需要通过构造函数注入得到一个类B的实例,而类B需要通过构造函数获得一个类A的实例。如果你为类A和类B配置了互相注入的bean,Spring IoC容器在运行时检测到循环引用,会抛出BeanCurrentlyInCreationException

一个可能的解决方案是编译某个类的源代码使其通过setter注入而不是构造函数注入。或者,避免构造函数注入仅用setter注入。换句话说,尽管是不被推荐的,但你可以通过setter注入配置循环依赖。

不像通常的情况(没有循环依赖),bean A和bean B之间的循环依赖可以强制其中的一个bean优先注入另一个bean中,可以使其完全初始化(古老的鸡/蛋场景)。

You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. This means that a Spring container which has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies. For example, the bean throws an exception as a result of a missing or invalid property. This potentially delayed visibility of some configuration issues is why ApplicationContext implementations by default pre-instantiate singleton beans. At the cost of some upfront time and memory to create these beans before they are actually needed, you discover configuration issues when the ApplicationContext is created, not later. You can still override this default behavior so that singleton beans will lazy-initialize, rather than be pre-instantiated.

通常情况下你可以信任Spring去做正确的事情。在容器加载时它检测配置问题,例如引用不存在的beans和循环依赖。当bean实际创建时,Spring设置属性和解析依赖尽可能的晚。这意味着Spring容器正确加载但后面可能会产生异常,当你请求一个对象时,创建对象或它的某个依赖时出现问题,这时容器就会抛出异常。例如,由于缺失或存在无效属性,bean会抛出异常。在真正需要这些beans之前创建它们,会花费一些前期时间和内存,但当ApplicationContext创建时你会发现配置问题,而不是在创建之后。为了单例bean延迟初始化而不是预先实例化,你仍需要重写这个默认行为。

If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.

如果没有循环依赖存在,当一个或更多协作beans注入到一个独立的bean中,在注入独立bean之前,每个协作bean都是完全配置的。这意味着如果bean A有个依赖为bean B,Spring IoC容器在调用bean A的setter方法之前会完整的配置bean B。换句话说,bean被实例化(不是预先实例化的单例),设置依赖和相关的生命周期方法(例如配置初始化方法或初始化bean回调方法)被调用。

Examples of dependency injection

The following example uses XML-based configuration metadata for setter-based DI. A small part of a Spring XML configuration file specifies some bean definitions:

下面的例子使用基于XML的配置元数据进行setter注入。Spring XML配置文件中的一小部分指定了一些bean的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>

<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ExampleBean {

private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;

public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}

public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}

public void setIntegerProperty(int i) {
this.i = i;
}

}

In the preceding example, setters are declared to match against the properties specified in the XML file. The following example uses constructor-based DI:

在上面的例子中,setter声明匹配XML文件中指定的属性。下面的例子使用了基于构造函数的依赖注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>

<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>

<constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ExampleBean {

private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;

public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}

}

The constructor arguments specified in the bean definition will be used as arguments to the constructor of the ExampleBean.

bean定义中指定的构造函数参数将作为ExampleBean的构造函数参数使用。

Now consider a variant of this example, where instead of using a constructor, Spring is told to call a static factory method to return an instance of the object:

现在考虑这个例子的一个变种,不使用构造函数,而是Spring调用静态工厂方法返回对象的一个实例:

1
2
3
4
5
6
7
8
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ExampleBean {

// a private constructor
private ExampleBean(...) {
...
}

// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}

}

Arguments to the static factory method are supplied via <constructor-arg/> elements, exactly the same as if a constructor had actually been used. The type of the class being returned by the factory method does not have to be of the same type as the class that contains the static factory method, although in this example it is. An instance (non-static) factory method would be used in an essentially identical fashion (aside from the use of the factory-bean attribute instead of the class attribute), so details will not be discussed here.

静态工厂方法的参数通过<constructor-arg/>元素提供,与构造函数使用的完全一样。虽然这个例子中工厂方法返回值的类型与包含静态工厂方法的类的类型一样,但它们可以不一样。工厂方法的实例(非静态)的使用本质上样式完全一样(除了使用factory-bean属性代替class属性之外),因此这儿不讨论这些细节。

3.4.2 Dependencies and configuration in detail

As mentioned in the previous section, you can define bean properties and constructor arguments as references to other managed beans (collaborators), or as values defined inline. Spring’s XML-based configuration metadata supports sub-element types within its <property/> and <constructor-arg/> elements for this purpose.

正如上一节提到的那样,你可以通过引用其它被管理bean(协作者)来定义bean的属性和构造函数参数,或者在行内定义值。为了实现这个功能,Spring的基于XML的配置元数据在它的<property/><constructor-arg/>中支持子元素类型。

Straight values (primitives, Strings, and so on)

The value attribute of the <property/> element specifies a property or constructor argument as a human-readable string representation. Spring’s conversion service is used to convert these values from a String to the actual type of the property or argument.

<property/>元素的value属性指定了一个属性或构造函数参数作为可读的字符串表示。使用Spring的转换服务将这些值从String转成属性或参数的真实类型。

1
2
3
4
5
6
7
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>

The following example uses the p-namespace for even more succinct XML configuration.

下面的例子为了更简洁的XML配置使用了p命名空间.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/>

</beans>

The preceding XML is more succinct; however, typos are discovered at runtime rather than design time, unless you use an IDE such as IntelliJ IDEA or the Spring Tool Suite (STS) that support automatic property completion when you create bean definitions. Such IDE assistance is highly recommended.

上面的XML是更简洁的;然而,错别字是在运行时发现而不是在设计时,除非你使用IDE例如IntelliJ IDEASpring Tool Suite (STS),当你创建bean定义时它们支持自动的属性补全。IDE辅助是强烈推荐的。

You can also configure a java.util.Properties instance as:

你也可以配置java.util.Properties实例:

1
2
3
4
5
6
7
8
9
10
11
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

<!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>

The Spring container converts the text inside the <value/>element into a java.util.Properties instance by using the JavaBeans PropertyEditor mechanism. This is a nice shortcut, and is one of a few places where the Spring team do favor the use of the nested <value/> element over the value attribute style.

Spring容器通过JavaBeans的PropertyEditor机制将<value/>元素内部的文本转成java.util.Properties实例。这是一个很好的捷径,使用嵌入的<value/>元素而不是使用value属性的方式,是Spring团队支持的几个地方之一。

The idref element

The idref element is simply an error-proof way to pass the id (string value - not a reference) of another bean in the container to a <constructor-arg/> or <property/> element.

在容器中传递另一个bean的id(字符串值,不是引用)到<constructor-arg/><property/>元素时,idref元素是一种简单的的误差检验方式。

1
2
3
4
5
6
7
<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" />
</property>
</bean>

The above bean definition snippet is exactly equivalent (at runtime) to the following snippet:

上面的bean定义片段与下面的代码片段是等价的(运行时):

1
2
3
4
5
<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
<property name="targetName" value="theTargetBean" />
</bean>

The first form is preferable to the second, because using the idref tag allows the container to validate at deployment time that the referenced, named bean actually exists. In the second variation, no validation is performed on the value that is passed to the targetName property of the client bean. Typos are only discovered (with most likely fatal results) when the client bean is actually instantiated. If the client bean is a prototype bean, this typo and the resulting exception may only be discovered long after the container is deployed.

第一种形式优于第二种形式,因为idref标签允许容器在部署时验证引用的bean,即命名的bean实际存在。在第二种形式中,当值传给clienttargetName时没有进行验证。拼写错误只有在clientbean实际创建时才会发现(最可能有严重后果)。如果client bean是原型bean,拼写错误和产生的异常可能只有在容器部署很长时间之后才会发现。

The local attribute on the idref element is no longer supported in the 4.0 beans xsd since it does not provide value over a regular bean reference anymore. Simply change your existing idref local references to idref bean when upgrading to the 4.0 schema.

 

idref元素的local属性在4.0 beans xsd中不再支持,因为它不再为合格的bean引用提供值。简单将你现有的idref local引用改成idref bean当更新到4.0 schema时。

A common place (at least in versions earlier than Spring 2.0) where the <idref/> element brings value is in the configuration of AOP interceptors in a ProxyFactoryBean bean definition. Using <idref/> elements when you specify the interceptor names prevents you from misspelling an interceptor id.

<idref/>元素带来值的通常位置(至少在Spring 2.0之前)是在ProxyFactoryBean bean定义中的AOP拦截器配置中。当你指定拦截器名字时使用<idref/>元素来防止误拼拦截器id。

References to other beans (collaborators)

The ref element is the final element inside a <constructor-arg/> or <property/> definition element. Here you set the value of the specified property of a bean to be a reference to another bean (a collaborator) managed by the container. The referenced bean is a dependency of the bean whose property will be set, and it is initialized on demand as needed before the property is set. (If the collaborator is a singleton bean, it may be initialized already by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the id/name of the other object through the bean, local, or parent attributes.

ref元素是<constructor-arg/><property/>定义元素的最终的元素。在这个元素中设置bean的指定属性的值,值为容器管理的另一个bean(协作bean)的引用。引用的bean是设置属性bean的依赖,在属性设置之前引用bean需要进行初始化。(如果协作bean是一个单例模式的bean,它可能已经被容器初始化了。)所有引用bean根本上都是另一个对象的引用。作用域和验证是根据你是否通过beanlocal,或parent属性指定了另一个对象的id/name来决定的。

Specifying the target bean through the bean attribute of the <ref/> tag is the most general form, and allows creation of a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file. The value of the bean attribute may be the same as the id attribute of the target bean, or as one of the values in the name attribute of the target bean.

通过<ref/>标签的bean属性指定目标bean是最常用的形式,允许创建同容器或父容器中任何bean的引用,不管它是否是在同一个XML文件中。bean属性的值可能与目标bean的id属性值相同,或与目标bean的name属性值相同。

1
<ref bean="someBean"/>

Specifying the target bean through the parent attribute creates a reference to a bean that is in a parent container of the current container. The value of the parent attribute may be the same as either the id attribute of the target bean, or one of the values in the name attribute of the target bean, and the target bean must be in a parent container of the current one. You use this bean reference variant mainly when you have a hierarchy of containers and you want to wrap an existing bean in a parent container with a proxy that will have the same name as the parent bean.

通过parent属性指定目标bean会引用当前容器的父容器中的bean。parent属性的值可能与目标bean的id值或name值相同,目标bean必须在当前容器的父容器中。当你有一个容器分层的时候你可以使用parent,你想将现有bean包裹在有代理的父容器中且现有bean与父容器中的bean同名,你可以使用parent属性。

1
2
3
4
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean>
1
2
3
4
5
6
7
8
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>

The local attribute on the ref element is no longer supported in the 4.0 beans xsd since it does not provide value over a regular bean reference anymore. Simply change your existing ref local references to ref bean when upgrading to the 4.0 schema.

 

idref元素的local属性在4.0 beans xsd中不再支持,因为它不再为合格的bean引用提供值。简单将你现有的idref local引用改成idref bean当更新到4.0 schema时。

Inner beans

A <bean/> element inside the <property/> or <constructor-arg/> elements defines a so-called inner bean.

<property/><constructor-arg/>元素内的<bean/>元素中定义bean称为内部bean。

1
2
3
4
5
6
7
8
9
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>

An inner bean definition does not require a defined id or name; if specified, the container does not use such a value as an identifier. The container also ignores the scope flag on creation: Inner beans are always anonymous and they are always created with the outer bean. It is not possible to inject inner beans into collaborating beans other than into the enclosing bean or to access them independently.

内部bean定义不要求定义id或name;如果指定了,容器不用用这个值作为标识符。容器创建时也忽略scope标记:内部bean总是匿名的且它们总是由外部bean创建。除了注入到封闭bean中或独立的访问它们,不可能将内部bean注入到协作bean中。

As a corner case, it is possible to receive destruction callbacks from a custom scope, e.g. for a request-scoped inner bean contained within a singleton bean: The creation of the inner bean instance will be tied to its containing bean, but destruction callbacks allow it to participate in the request scope’s lifecycle. This is not a common scenario; inner beans typically simply share their containing bean’s scope.

作为一种很少出现的情况,从特定的域中有可能会收到销毁回调函数,例如,对于请求域内的内部bean包含单例bean:内部bean实例的创建会绑定到它的包含bean,但销毁回调函数允许它进入到请求域的生命周期中。这不是一个常见的场景;内部bean通常简单的共享它们的包含bean的作用域。

Collections

In the <list/>, <set/>, <map/>, and <props/> elements, you set the properties and arguments of the Java Collection types List, Set, Map, and Properties, respectively.

<list/><set/><map/><props/>元素中,你要分别设置Java Collection类型listsetmapProperties的属性和参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>

The value of a map key or value, or a set value, can also again be any of the following elements:

map的key或value,或者是set value的值也可以是下面元素中的任何一个:

1
bean | ref | idref | list | set | map | props | value | null

Collection merging

The Spring container also supports the merging of collections. An application developer can define a parent-style <list/>, <map/>, <set/> or <props/> element, and have child-style <list/>, <map/>, <set/> or <props/> elements inherit and override values from the parent collection. That is, the child collection’s values are the result of merging the elements of the parent and child collections, with the child’s collection elements overriding values specified in the parent collection.

Spring也支持集合的合并。应用开发者可以定义父类型<list/><map/><set/><props/>元素,可以有继承和覆盖父集合的子类型元素<list/><map/><set/><props/>。也就是说,子集合的值是父集合和子集合中元素合并的结果,子集合元素覆盖了父集合元素的值。

This section on merging discusses the parent-child bean mechanism. Readers unfamiliar with parent and child bean definitions may wish to read the relevant section before continuing.

关于合并的这节讨论了父子bean机制。对父子bean定义不熟悉的读者可以去读相关的章节。

The following example demonstrates collection merging:

下面的例子示范了集合合并:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>

Notice the use of the merge=true attribute on the <props/> element of the adminEmails property of the child bean definition. When the child bean is resolved and instantiated by the container, the resulting instance has an adminEmails Properties collection that contains the result of the merging of the child’s adminEmails collection with the parent’s adminEmails collection.

注意child bean定义中的adminEmails属性下的<props/>元素使用了merge=true属性。当容器解析并实例化child bean时,最终的实例含有adminEmails Properties集合,集合中的值是子adminEmails集合和父adminEmails集合合并的结果。

1
2
3
administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk

The child Properties collection’s value set inherits all property elements from the parent <props/>, and the child’s value for the support value overrides the value in the parent collection.

Properties集合的值继承了父<props/>中的所有属性元素,子集合中的support值覆盖了父集合中的值。

This merging behavior applies similarly to the <list/>, <map/>, and <set/> collection types. In the specific case of the <list/> element, the semantics associated with the List collection type, that is, the notion of an ordered collection of values, is maintained; the parent’s values precede all of the child list’s values. In the case of the Map, Set, and Properties collection types, no ordering exists. Hence no ordering semantics are in effect for the collection types that underlie the associated Map, Set, and Properties implementation types that the container uses internally.

<list/><map/><set/>集合类型中的合并与上面类似。在特定的<list/>元素情况下,关于List集合类型的语义,也就是说,有序集合值的概念仍然是保留的;父list中的值领先于所有子list中的值。在MapSetProperties集合类型,不存在顺序。因此,无序语义在容器内部使用的集合类型MapSetProperties的实现基础上是有效的。

Limitations of collection merging

You cannot merge different collection types (such as a Map and a List), and if you do attempt to do so an appropriate Exception is thrown. The merge attribute must be specified on the lower, inherited, child definition; specifying the merge attribute on a parent collection definition is redundant and will not result in the desired merging.

你不能合并不同的集合类型(例如MapList),如果你试图合并不同的集合类型会有适当的抛出Exceptionmerge属性必须在更低的、继承的子定义中;在父集合定义中指定merge属性是多余的并且不会进行合并。

Strongly-typed collection

With the introduction of generic types in Java 5, you can use strongly typed collections. That is, it is possible to declare a Collection type such that it can only contain String elements (for example). If you are using Spring to dependency-inject a strongly-typed Collection into a bean, you can take advantage of Spring’s type-conversion support such that the elements of your strongly-typed Collection instances are converted to the appropriate type prior to being added to the Collection.

随着Java 5中泛型的引入,你可以使用强类型集合。也就是说,你可以声明一个Collection类型但它只能包含String元素(例子)。如果你使用Spring将一个强类型的Collection注入到bean中,你可以利用Spring的类型转换支持,例如在将元素添加到Collection之前,将你的强类型Collection实例中的元素转成恰当的类型。

1
2
3
4
5
6
7
8
public class Foo {

private Map<String, Float> accounts;

public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
1
2
3
4
5
6
7
8
9
10
11
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>

When the accounts property of the foo bean is prepared for injection, the generics information about the element type of the strongly-typed Map<String, Float> is available by reflection. Thus Spring’s type conversion infrastructure recognizes the various value elements as being of type Float, and the string values 9.99, 2.75, and 3.99 are converted into an actual Float type.

当注入foo bean的accounts属性时,强类型Map<String, Float>中元素类型的泛型信息可以通过反射得到。因此Spring的类型转换结构能识别各种值元素的类型为Float,字符串9.99, 2.753.99会被转换成实际的Float类型。

Null and empty string values

Spring treats empty arguments for properties and the like as empty Strings. The following XML-based configuration metadata snippet sets the email property to the empty String value (“”).

Spring把属性的空参数都处理为空Strings。下面基于XML的配置元数据片段将email属性设为空String值(“”)。

1
2
3
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>

The preceding example is equivalent to the following Java code:

上面的例子与下面的Java代码是等价的:

1
exampleBean.setEmail("")

The <null/> element handles null values. For example:

<null/>元素处理null值。例如:

1
2
3
4
5
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>

The above configuration is equivalent to the following Java code:

上面的配置与下面的Java代码等价。

1
exampleBean.setEmail(null)

XML shortcut with the p-namespace

The p-namespace enables you to use the bean element’s attributes, instead of nested <property/> elements, to describe your property values and/or collaborating beans.

p命名空间可以让你不需要嵌入<property/>元素便能使用bean元素的属性来描述你的属性值以及/或协作beans。

Spring supports extensible configuration formats with namespaces, which are based on an XML Schema definition. The beans configuration format discussed in this chapter is defined in an XML Schema document. However, the p-namespace is not defined in an XSD file and exists only in the core of Spring.

Spring支持含有命名空间的扩展配置形式,命名控件是基于XML Schema定义的。本章讨论的beans配置形式是在XML Schema文档中定义的。但是p命名空间不能在XSD文件中定义并且只在Spring core中存在。

The following example shows two XML snippets that resolve to the same result: The first uses standard XML format and the second uses the p-namespace.

下面的例子显示了两个XML片段,解析结果是相同的:第一个是标准的XML形式,第二个使用了p命名空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="foo@bar.com"/>
</bean>

<bean name="p-namespace" class="com.example.ExampleBean"
p:email="foo@bar.com"/>
</beans>

The example shows an attribute in the p-namespace called email in the bean definition. This tells Spring to include a property declaration. As previously mentioned, the p-namespace does not have a schema definition, so you can set the name of the attribute to the property name.

这个例子显示了bean定义中p命名空间中有个一个叫email的属性。这会通知Spring包含属性声明。如前面所述,p命名空间没有schema定义,因此你可以将特性值(attribute)设到属性值(property)上。

This next example includes two more bean definitions that both have a reference to another bean:

下面的例子包括两个bean定义,且它们都引用了另一个bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>

<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>

<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>

As you can see, this example includes not only a property value using the p-namespace, but also uses a special format to declare property references. Whereas the first bean definition uses <property name="spouse" ref="jane"/> to create a reference from bean john to bean jane, the second bean definition uses p:spouse-ref="jane" as an attribute to do the exact same thing. In this case spouse is the property name, whereas the -ref part indicates that this is not a straight value but rather a reference to another bean.

正如你所看到的,这个例子不仅包括使用了p命名空间的属性值,而且使用了一种特定的形式来声明属性引用。然而第一个bean定义使用<property name="spouse" ref="jane"/>创建了一个从bean john到bean jane的引用,第二个bean定义使用p:spouse-ref="jane"作为一个特性同样定义了从bean john到bean jane的引用。在spouse是属性名的情况下,-ref部分表示这不是一个直接的值而是另一个bean的引用。

The p-namespace is not as flexible as the standard XML format. For example, the format for declaring property references clashes with properties that end in Ref, whereas the standard XML format does not. We recommend that you choose your approach carefully and communicate this to your team members, to avoid producing XML documents that use all three approaches at the same time.

 

p命名空间不是标准的XML格式,例如,声明的属性引用会与以Ref结尾的属性相冲突,而标准XML格式则不会。我们建议你仔细的选择你的方法并与你的团队成员交流,避免生成的XML文档同时使用了三种方式。

XML shortcut with the c-namespace

Similar to the the section called “XML shortcut with the p-namespace”, the c-namespace, newly introduced in Spring 3.1, allows usage of inlined attributes for configuring the constructor arguments rather then nested constructor-arg elements.

与“XML shortcut with the p-namespace”小节类似,在Spring 3.1新引入的c命名空间允许使用行内属性配置构造函数参数而不用嵌入constructor-arg元素。

Let’s review the examples from the section called “Constructor-based dependency injection” with the c: namespace:

让我们重新回顾一下“Constructor-based dependency injection”小节中的例子并使用c:命名空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>

<!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
<constructor-arg value="foo@bar.com"/>
</bean>

<!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>

</beans>

The c: namespace uses the same conventions as the p: one (trailing -ref for bean references) for setting the constructor arguments by their names. And just as well, it needs to be declared even though it is not defined in an XSD schema (but it exists inside the Spring core).

c:命名空间遵循与p:命名空间相同的约定在通过名字设置构造函数参数时。同样的,它也需要进行声明,虽然它不能在XSD schema中使用(但在Spring core中存在)。

For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), one can use fallback to the argument indexes:

对于很少出现的不能找到构造函数参数名字的情况(通常如果编译字节码且没有调试信息),可以使用参数索引:

1
2
<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>

Due to the XML grammar, the index notation requires the presence of the leading _ as XML attribute names cannot start with a number (even though some IDE allow it).

 

由于XML语法,索引符号需要前面加上_,因为XML属性名字不能以数字开头(即使一些IDE允许)。

In practice, the constructor resolution mechanism is quite efficient in matching arguments so unless one really needs to, we recommend using the name notation through-out your configuration.

在实践中,构造函数解析机制能有效匹配参数,因此除非真的需要,否则我们推荐在配置中使用名字符号。

Compound property names

You can use compound or nested property names when you set bean properties, as long as all components of the path except the final property name are not null. Consider the following bean definition.

当你设置bean属性时,你可以使用混合的或嵌入的属性名字,只要路径中除了最后的属性名之外所有组件都是非null。考虑下面的bean定义。

1
2
3
<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>

The foo bean has a fred property, which has a bob property, which has a sammy property, and that final sammy property is being set to the value 123. In order for this to work, the fred property of foo, and the bob property of fred must not be null after the bean is constructed, or a NullPointerException is thrown.

foobean有一个fred属性,fred有一个sammy属性,bob有一个sammy属性,最后的sammy属性设置值为123。为了这样设置,foofred属性,fredbob属性在bean创建后必须是非null或抛出NullPointerException

3.4.3 Using depends-on

If a bean is a dependency of another that usually means that one bean is set as a property of another. Typically you accomplish this with the <ref/> element in XML-based configuration metadata. However, sometimes dependencies between beans are less direct; for example, a static initializer in a class needs to be triggered, such as database driver registration. The depends-on attribute can explicitly force one or more beans to be initialized before the bean using this element is initialized. The following example uses the depends-on attribute to express a dependency on a single bean:

如果一个bean是另一个bean的一个依赖,这通常意味着一个bean作为另一个bean的一个属性去设置。在基于XML的配置元数据中通常使用<ref/>元素实现。然而有时beans之间的依赖关系是间接的;例如,类中的静态初始化程序需要触发,例如数据驱动注册。depends-on特性能显示的强制一个bean或多个beans在使用这个元素的bean初始化之前进行初始化。下面的例子使用depends-on特性表示一个单一bean的一个依赖:

1
2
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

To express a dependency on multiple beans, supply a list of bean names as the value of the depends-on attribute, with commas, whitespace and semicolons, used as valid delimiters:

为了表示多个bean上的依赖关系,提供一个bean名字列表作为depends-on特性的值,用逗号,空格或分号作为有效分隔符:

1
2
3
4
5
6
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

The depends-on attribute in the bean definition can specify both an initialization time dependency and, in the case of singleton beans only, a corresponding destroy time dependency. Dependent beans that define a depends-on relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thus depends-on can also control shutdown order.

 

depends-on特性在bean定义中可以指定初始化时的依赖和对应的销毁时依赖(仅在单例情况下)。依赖beans与给定bean之间定义了一个depends-on关系,依赖beans在给定bean本身被销毁之前首先被销毁。因此depends-on也可以控制销毁顺序。

3.4.4 Lazy-initialized beans

By default, ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.

默认情况下,作为初始化过程的一部分,ApplicationContext实现时渴望创建并配置所有的单例beans。通常情况下,预实例化是必要的,因为配置中或周围环境中的错误可以立即发现,与几小时或几天后发现截然相反。当预实例化是不必要的时候,你可通过标记bean定义为延迟初始化来阻止单例bean的预实例化。延迟初始化的bean会通知IoC容器当第一次请求bean时创建一个bean实例,而不是在启动时创建。

In XML, this behavior is controlled by the lazy-init attribute on the <bean/> element; for example:

在XML中,延迟初始化通过<bean/>元素中的lazy-init特性来控制;例如:

1
2
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>

When the preceding configuration is consumed by an ApplicationContext, the bean named lazy is not eagerly pre-instantiated when the ApplicationContext is starting up, whereas the not.lazy bean is eagerly pre-instantiated.

ApplicationContext读取到上面的配置,ApplicationContext启动时名字为lazy的bean不会进行预实例化,而名字为not.lazy的bean会进行预实例化。

However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the ApplicationContext creates the lazy-initialized bean at startup, because it must satisfy the singleton’s dependencies. The lazy-initialized bean is injected into a singleton bean elsewhere that is not lazy-initialized.

然而,当延迟初始化的bean是一个非延迟初始化的单例bean的依赖时,ApplicationContext会在启动时创建延迟初始化的bean,因为它必须提供单例bean的依赖。延迟初始化的bean会注入到单例bean中,而在其它地方它是非延迟初始化的。

You can also control lazy-initialization at the container level by using the default-lazy-init attribute on the <beans/> element; for example:

你也可以在容器中通过<beans/>中的default-lazy-init特性控制延迟初始化;例如:

1
2
3
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>

3.4.5 Autowiring collaborators

The Spring container can autowire relationships between collaborating beans. You can allow Spring to resolve collaborators (other beans) automatically for your bean by inspecting the contents of the ApplicationContext. Autowiring has the following advantages:

  • Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.)

  • Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.

Spring容器能自动装配协作beans之间的关联关系。你可以允许Spring通过检查ApplicationContext中的内容自动的为你的bean解析协作者(其它bean)。自动装配有以下优势:

  • 自动装配能明显减少指定属性或构造函数参数的需要。(其它的机制例如在本章其它地方讨论的bean模板在这一点上也是非常重要的。)

  • 当对象变化时自动装配能更新配置。例如,如果你需要增加一个类的依赖项,依赖项可以是满足自动装配的而不需要你去修改配置。因此自动装配在开发时尤其有用,当代码基础变的更稳定时可以改为显式装配。

When using XML-based configuration metadata, you specify autowire mode for a bean definition with the autowire attribute of the <bean/> element. The autowiring functionality has four modes. You specify autowiring per bean and thus can choose which ones to autowire.

当使用基于XML的配置元数据时,通过使用<bean/>元素的autowire特性你可以指定一个bean定义的自动装配模式。自动注入功能有四种模式。你可以指定每个bean的自动装配模式,因此你可以选择使用哪一种模式。

Table 3.2. Autowiring modes

Mode Explanation
no (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
byName Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property.
byType Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.
constructor Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.

表 3.2 自动装配模式

模式 解析
no (默认)无自动装配。引用bean必须通过ref元素定义。对于更大的部署,不推荐更改默认设置,因为显式指定协作者更清晰并且更易控制。在某种程度上来说,它记录了系统的结构。
byName 通过属性名称自动装配。Spring寻找与需要自动装配的属性同名的bean。例如,如果一个bean定义设置为通过名称自动装配,它有一个master属性(也就是说,它有一个setMaster(..)方法),Spring寻找名字为master的bean定义,使用它设置属性值。
byType 如果容器中含有属性类型已知的一个bean,那么可以允许按类型自动装配属性。如果此类型的bean不止一个,则会抛出致命的异常,这意味着你可能不能使用byType来注入那个bean。如果没有匹配的bean,则什么也不做;属性没有被设置。
constructor byType类似,但是应用到构造函数参数上的。如果容器中没有一个构造函数参数bean的确定类型,将会抛出一个致命的异常。

With byType or constructor autowiring mode, you can wire arrays and typed-collections. In such cases all autowire candidates within the container that match the expected type are provided to satisfy the dependency. You can autowire strongly-typed Maps if the expected key type is String. An autowired Maps values will consist of all bean instances that match the expected type, and the Maps keys will contain the corresponding bean names.

通过byType或构造函数自动装配模式,你可以配置数组和集合类型。在这种情况下容器内所有能匹配期望类型的自动装配候选对象将被提供合适的依赖项。如果期望的key类型是String类型,你可以自动装配强类型的Maps。自动装配的Maps的值将有所有匹配期望类型的bean组成,Maps的键将包含对应的bean名称。

You can combine autowire behavior with dependency checking, which is performed after autowiring completes.

你可以将依赖检查与自动装配相结合,它将在自动装配完成之后执行。

Limitations and disadvantages of autowiring

Autowiring works best when it is used consistently across a project. If autowiring is not used in general, it might be confusing to developers to use it to wire only one or two bean definitions.

当自动装配在整个工程中一致的使用时其效果最好。如果通常情况下不使用自动装配,仅在一两个bean定义中使用自动装配开发人员可能感到非常困惑。

Consider the limitations and disadvantages of autowiring:

  • Explicit dependencies in property and constructor-arg settings always override autowiring. You cannot autowire so-called simple properties such as primitives, Strings, and Classes (and arrays of such simple properties). This limitation is by-design.

  • Autowiring is less exact than explicit wiring. Although, as noted in the above table, Spring is careful to avoid guessing in case of ambiguity that might have unexpected results, the relationships between your Spring-managed objects are no longer documented explicitly.

  • Wiring information may not be available to tools that may generate documentation from a Spring container.

  • Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Maps, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown.

考虑一下自动装配的限制与缺点:

  • propertyconstructor-arg中显式依赖的设置总是会覆盖自动装配。你不能自动装配所谓的简单属性例如基本类型,StringsClasses(和简单类型的数组)。这是设计上的限制。

  • 与显式配置相比,自动装配是更不确定的。尽管Spring小心的避免猜测以防歧义性引起无法预料的后果,但Spring管理的对象之间的关系不再被显式的记录。

  • Spring容器中能产生文档的工具可能得不到配置信息。

  • setter方法或构造函数参数指定的类型进行自动装配时可能匹配到容器中多个bean的定义。对于数组,集合或Maps而言,这是一个不必要的问题。然而对于只期望一个值的依赖而言,这个歧义性不能任意解决。如果不能获得唯一的bean定义,会抛出异常。

In the latter scenario, you have several options:

  • Abandon autowiring in favor of explicit wiring.

  • Avoid autowiring for a bean definition by setting its autowire-candidate attributes to false as described in the next section.

  • Designate a single bean definition as the primary candidate by setting the primary attribute of its <bean/> element to true.

  • Implement the more fine-grained control available with annotation-based configuration, as described in Section 3.9, “Annotation-based container configuration”.

后面的方案中,你有一些选择:

  • 放弃自动装配支持显式配置。

  • 通过设置bean的autowire-candidate特性为false来避免自动装配。

  • 通过设置<bean/>元素的primary特性为true来指定一个单例bean定义作为主要的候选bean。

  • 通过基于注解的配置实现更多细颗粒的控制,如3.9小节 “基于注解的容器配置”。

Excluding a bean from autowiring

On a per-bean basis, you can exclude a bean from autowiring. In Spring’s XML format, set the autowire-candidate attribute of the <bean/> element to false; the container makes that specific bean definition unavailable to the autowiring infrastructure (including annotation style configurations such as @Autowired).

在单个bean的基础上,你可以排除bean在自动装配之外。在Spring的XML形式中,设置<bean/>元素的autowire-candidate特性为false;容器会使自动装配基础框架不能得到指定bean定义(包括注解类型的配置,例如@Autowired)。

You can also limit autowire candidates based on pattern-matching against bean names. The top-level <beans/> element accepts one or more patterns within its default-autowire-candidates attribute. For example, to limit autowire candidate status to any bean whose name ends with Repository, provide a value of *Repository. To provide multiple patterns, define them in a comma-separated list. An explicit value of true or false for a bean definitions autowire-candidate attribute always takes precedence, and for such beans, the pattern matching rules do not apply.

你也可以根据bean名称的匹配模式限制自动装配的候选目标。顶层的<beans/>元素可以接收default-autowire-candidates特性中的一个或多个模式。例如,为了限制自动装配候选目标匹配任何名字以Repository结尾的bean,可以提供一个*Repository值。为了提供多种模式,可以定义一个以逗号为分隔符的列表。bean定义中autowire-candidate特性显示的值truefalse最是优先起作用的,对于这些bean而言,模式匹配规则不起作用。

These techniques are useful for beans that you never want to be injected into other beans by autowiring. It does not mean that an excluded bean cannot itself be configured using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.

这些技术对于那些你从不想通过自动装配方式注入到其它bean中的beans而言是很有用的。这不意味着一个排除的bean它本身不能通过自动装配进行配置。更确切的说,bean本身不是一个进行其它bean进行自动装配的候选者。

3.4.6 Method injection

In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.

在大多数应用场景中,容器中的大多数bean是单例的。当一个单例bean需要与另一个单例bean协作时,或一个非单例bean需要与另一个非单例bean协作时,你通常通过定义一个bean作为另一个bean的一个属性来处理这个依赖关系。当bean的生命周期不同时问题就出现了。假设一个单例bean A需要使用非单例(标准)bean B时,也许A中的每一个方法调用都要使用bean B。容器仅创建单例bean A一次,因此仅有一次设置属性的机会。容器不能在每次需要bean B时提供一个bean B的新的实例。

A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean(“B”) call to the container ask for (a typically new) bean B instance every time bean A needs it. The following is an example of this approach:

一个解决方案是放弃一些控制反转。你可以使bean A通过实现ApplicationContextAware接口感知到容器,每个bean A需要的时候就通过getBean("B")调用向容器请求(通常是新的)一个bean B的实例。下面是这种方法的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

private ApplicationContext applicationContext;

public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}

protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}

public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion.

前面所讲的不是让人满意的,因为业务代码能感知并耦合了Spring框架。方法注入,Spring IoC容器的一个有点高级的特性,允许使用一种干净的方式来处理这个案例。

You can read more about the motivation for Method Injection in this blog entry.

 

你可以在blog entry中了解更多关于方法注入的动机。

Lookup method injection

Lookup method injection is the ability of the container to override methods on container managed beans, to return the lookup result for another named bean in the container. The lookup typically involves a prototype bean as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to generate dynamically a subclass that overrides the method.

查找方法注入是容器的一种覆盖其管理的beans中的方法的能力,可以返回容器中另一个命名bean查找结果。查找通常会涉及到一个标准bean,如前一小节中讲的那样。Spring框架实现了查找方法注入,它是通过使用CGLIB库生成的字节码来动态的产生一个覆盖这个方法的子类。

  • For this dynamic subclassing to work, the class that the Spring bean container will subclass cannot be final, and the method to be overridden cannot be final either.

  • Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.

  • Concrete methods are also necessary for component scanning which requires concrete classes to pick up.

  • A further key limitation is that lookup methods won’t work with factory methods and in particular not with @Bean methods in configuration classes, since the container is not in charge of creating the instance in that case and therefore cannot create a runtime-generated subclass on the fly.

  • Finally, objects that have been the target of method injection cannot be serialized.

 

  • 为了使动态子类化起作用,Spring bean容器要进行子类化的类不能是最终的类,要进行重写的方法也不是最终的方法。

  • 单元测试一个含有抽象方法的类需要你自己对这个类进行子类化,并且提供这个抽象方法的stub实现。

  • 实体方法对于要求获得实体类的组件扫描也是必需的。

  • 一个更关键的限制是查找方法不能与工厂方法一起工作,尤其是在配置类中不能与@Bean方法同时起作用,由于那种情况下容器不能控制实例的创建,因此不能在飞速写入中创建一个运行时产生的子类。

  • 最后,方法注入的目标对象不能被序列化。

Looking at the CommandManager class in the previous code snippet, you see that the Spring container will dynamically override the implementation of the createCommand() method. Your CommandManager class will not have any Spring dependencies, as can be seen in the reworked example:

看一下前面代码片中的CommandManager类,你可以看到Spring容器将会动态的覆盖createCommand()方法的实现。CommandManager类不会有任何Spring依赖,重写的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}

// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}

In the client class containing the method to be injected (the CommandManager in this case), the method to be injected requires a signature of the following form:

客户类中包含要注入的方法(在这个例子中是CommandManager),要注入的方法需要下面形式的一个签名:

1
<public|protected> [abstract] <return-type> theMethodName(no-arguments);

If the method is abstract, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class. For example:

如果这个方法是抽象的,动态产生的子类会实现这个方法。另外,动态产生的子类会覆盖原来的类中定义的实体方法。例如:

1
2
3
4
5
6
7
8
9
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="command"/>
</bean>

The bean identified as commandManager calls its own method createCommand() whenever it needs a new instance of the command bean. You must be careful to deploy the command bean as a prototype, if that is actually what is needed. If it is deployed as a singleton, the same instance of the command bean is returned each time.

无论什么时候识别为commandManager的bean需要一个command bean的新实例,它都会调用它的createCommand()方法。如果真的需要的话,你必须小心的部署command bean为一个原型。如果它被部署为一个单例,每次都会返回同一个command实例。

The interested reader may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be of use. The approach used in ServiceLocatorFactoryBean is similar to that of another utility class, ObjectFactoryCreatingFactoryBean, but it allows you to specify your own lookup interface as opposed to a Spring-specific lookup interface. Consult the javadocs of these classes for additional information.

 

感兴趣的读者可能也会发现ServiceLocatorFactoryBean(在org.springframework.beans.factory.config包中)使用这种方法。ServiceLocatorFactoryBean中使用的方法与另一个工具类ObjectFactoryCreatingFactoryBean中的方法类似,但它允许你指定你自己的查找接口,与Spring特定的查找接口相反。这些类的额外信息请查询Java文档。

Arbitrary method replacement

A less useful form of method injection than lookup method injection is the ability to replace arbitrary methods in a managed bean with another method implementation. Users may safely skip the rest of this section until the functionality is actually needed.

一种比查找方法注入更少使用的形式是用另一种方法实现替换管理的bean中任意方法的能力。用户可以安全跳过本节剩下的部分,直到这个方法真正需要的时候再看。

With XML-based configuration metadata, you can use the replaced-method element to replace an existing method implementation with another, for a deployed bean. Consider the following class, with a method computeValue, which we want to override:

在基于XML的配置元数据中,对于一个部署的bean,你可以通过replaced-method元素用另一个方法实现替换现有的方法实现。考虑下面的类,有一个我们想覆盖的computeValue方法:

1
2
3
4
5
6
7
8
9
public class MyValueCalculator {

public String computeValue(String input) {
// some real code...
}

// some other methods...

}

A class implementing the org.springframework.beans.factory.support.MethodReplacer interface provides the new method definition.

实现了org.springframework.beans.factory.support.MethodReplacer接口的类提供了一种新的方法定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {

public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}

The bean definition to deploy the original class and specify the method override would look like this:

部署最初的类的bean定义和指定的重写方法如下:

1
2
3
4
5
6
7
8
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

You can use one or more contained <arg-type/> elements within the <replaced-method/> element to indicate the method signature of the method being overridden. The signature for the arguments is necessary only if the method is overloaded and multiple variants exist within the class. For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, the following all match java.lang.String:

你可以在<replaced-method/>元素中使用一个或多个包含<arg-type/>元素来指出要覆盖的方法的方法签名。只有类中进行了方法重载且有多个重载变种的时候,参数的签名才是必需的。为了简便,字符串类型的参数可能是全拼类型名称的一个子串。例如,下面的所有写法都能匹配java.lang.String

1
2
3
java.lang.String
String
Str

Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by allowing you to type only the shortest string that will match an argument type.

因为参数数目经常是足够区分每个可能的选择的,通过允许定义匹配参数类型的最短字符串类型,这个缩写可以保存许多类型。

3.5 Bean scopes

When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition. The idea that a bean definition is a recipe is important, because it means that, as with a class, you can create many object instances from a single recipe.

当你创建bean定义时,你创建了一个配方用于创建bean定义中定义的类的实例。bean定义是配方的想法是很重要的,因为这意味着对于一个类,你可以根据一个配方创建许多对象实例。

You can control not only the various dependencies and configuration values that are to be plugged into an object that is created from a particular bean definition, but also the scope of the objects created from a particular bean definition. This approach is powerful and flexible in that you can choose the scope of the objects you create through configuration instead of having to bake in the scope of an object at the Java class level. Beans can be defined to be deployed in one of a number of scopes: out of the box, the Spring Framework supports six scopes, five of which are available only if you use a web-aware ApplicationContext.

你不仅能管理要插入对象中的的各种依赖和配置值,而且能管理对象的作用域,对象是从特定的bean定义中创建的。这种方法是强大且灵活的,你可以通过配置文件选择你创建的对象的作用域,从而代替Java类级别对象的内置作用域。定义的beans将部署成多种作用域中的一种:开箱即用,Spring框架支持六种作用域,如果你使用感知web的ApplicationContext,你只可以使用其中的五种作用域。

The following scopes are supported out of the box. You can also create a custom scope.

下面的作用域支持开箱即用。你也可以创建一个定制的作用域。

Table 3.3. Bean scopes

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance per Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request; that is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

表 3.3 bean作用域

作用域 描述
singleton (默认) 每个Spring IoC容器使单个bean定义只能创建一个对象实例。
prototype 单个bean定义可以创建任何数量的对象实例。
request 单个bean定义的创建实例的作用域为单个HTTP request的声明周期;也就是说,每个HTTP request有它自己的根据bean定义创建的实例。只在感知Spring ApplicationContext的上下文中有效。
session 单个bean定义的创建实例的作用域为HTTP Session的生命周期. 只在感知Spring ApplicationContext的上下文中有效。
application 单个bean定义的创建实例的作用域为ServletContext的生命周期。 只在感知Spring ApplicationContext的上下文中有效。
websocket 单个bean定义的创建实例的作用域为WebSocket的生命周期。 只在感知Spring ApplicationContext的上下文中有效。

As of Spring 3.0, a thread scope is available, but is not registered by default. For more information, see the documentation for SimpleThreadScope. For instructions on how to register this or any other custom scope, see the section called “Using a custom scope”.

 

从Spring 3.0,引入了thread scope作用域,但默认情况下是不注册的。更多的信息请看SimpleThreadScope文档。关于怎么注册thread scope作用域或任何其它的定制作用域的介绍,请看『Using a custom scope』小节。

3.5.1 The singleton scope

Only one shared instance of a singleton bean is managed, and all requests for beans with an id or ids matching that bean definition result in that one specific bean instance being returned by the Spring container.

单例bean只管理一个共享实例,id匹配bean定义的所有对beans的请求,Spring容器会返回一个特定的bean实例。

To put it another way, when you define a bean definition and it is scoped as a singleton, the Spring IoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object.

换言之,当你定义一个bean定义时,它的作用域为单例,Spring IoC容器会根据bean定义创建一个确定的对象实例。这个单独的实例存储在单例beans的缓存中,接下来的对这个命名bean的所有请求和引用都会返回那个缓存的对象。

image

Spring’s concept of a singleton bean differs from the Singleton pattern as defined in the Gang of Four (GoF) patterns book. The GoF Singleton hard-codes the scope of an object such that one and only one instance of a particular class is created per ClassLoader. The scope of the Spring singleton is best described as per container and per bean. This means that if you define one bean for a particular class in a single Spring container, then the Spring container creates one and only one instance of the class defined by that bean definition. The singleton scope is the default scope in Spring. To define a bean as a singleton in XML, you would write, for example:

Spring中的单例bean概念不同于《设计模式》书中定义的单例模式。设计模式中的单例是对对象的作用域进行硬编码,为的是每个类加载器只能创建一个特定类的实例。Spring单例作用域最好的描述是每个容器每个类。这意味着如果你在单个的Spring容器中为一个特定的类定义了一个bean,Spring只会根据bean定义创建一个类的实例。在Spring中单例作用域是默认的作用域。为了在XML定义一个单例bean,你可以像下面一样写,例如:

1
2
3
4
<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

3.5.2 The prototype scope

The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a getBean() method call on the container. As a rule, use the prototype scope for all stateful beans and the singleton scope for stateless beans.

非单例模式,bean部署采用原型作用域时,每次产生一个特定bean的请求时都会创建一个新的bean实例。也就是说,这个bean会注入到另一个bean中或你可以在容器中通过调用getBean()方法来请求它。通常,对于所有有状态的beans使用原型作用域,对于无状态的beans使用单例作用域。

The following diagram illustrates the Spring prototype scope. A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state; it was just easier for this author to reuse the core of the singleton diagram.

下面的图阐述了Spring原型作用域。数据访问对象(DAO)通常是不会配置为原型的,因为一个典型的DAO不会有任何会话状态;对于作者来说很容易重用单例图的核心。

image

The following example defines a bean as a prototype in XML:

下面的例子在XML中定义一个原型bean:

1
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>

In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that prototype instance. Thus, although initialization lifecycle callback methods are called on all objects regardless of scope, in the case of prototypes, configured destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding. To get the Spring container to release resources held by prototype-scoped beans, try using a custom bean post-processor, which holds a reference to beans that need to be cleaned up.

与其它作用域相比,Spring不管理原型bean的完整生命周期:容器初始化、配置,另外组装原型对象,并把它传递给客户端,之后不再记录原型实例。因此,虽然不管什么作用域初始化生命周期回调函数都会在所有对象上调用,但是在原型作用域的情况下,不会调用配置的销毁生命周期回调函数。客户端代码必须清理原型作用域的对象并释放原型bean拥有的昂贵资源。为了使Spring容器释放原型bean拥有的资源,尝试使用定制的bean后处理程序,它拥有需要清理的bean的引用。

In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client. (For details on the lifecycle of a bean in the Spring container, see Section 3.6.1, “Lifecycle callbacks”.)

在有些方面,关于原型作用域,Spring容器的角色像是Java中new操作符的替代品。所有生命周期的管理必须由客户端处理。(Spring容器中更多关于bean生命周期的细节,请看3.6.1小节,”生命周期回调”)。

3.5.3 Singleton beans with prototype-bean dependencies

When you use singleton-scoped beans with dependencies on prototype beans, be aware that dependencies are resolved at instantiation time. Thus if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.

当你使用含有原型bean依赖的单例作用域bean时,要意识到依赖解析是在实例化时。因此如果你使用依赖注入将原型作用域的bean注入到单例作用域的bean中时,将会实例化一个新的原型bean并依赖注入到单例bean中。原型bean实例曾经是唯一提供给单例作用域的bean的实例。

However, suppose you want the singleton-scoped bean to acquire a new instance of the prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies. If you need a new instance of a prototype bean at runtime more than once, see Section 3.4.6, “Method injection”.

假设你想在运行时让单例作用域的bean重复的获得原型作用域bean的新实例。你不能依赖注入原型作用域的bean到你的单例bean中,因为当Spring容器实例化单例bean,解析并注入它的依赖时,注入只发生一次。如果你在运行时不止一次需要原型bean的实例,请看3.4.6小节,”方法注入”。

3.5.4 Request, session, application, and WebSocket scopes

The request, session, application, and websocket scopes are only available if you use a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you use these scopes with regular Spring IoC containers such as the ClassPathXmlApplicationContext, an IllegalStateException will be thrown complaining about an unknown bean scope.

如果你使用感知web的Spring ApplicationContext实现(例如XmlWebApplicationContext),requestsessionapplicationwebsocket作用域是唯一可用的作用域。如果你通过正规的Spring IoC容器例如ClassPathXmlApplicationContext来使用这些作用域,会抛出IllegalStateException异常,投诉使用了一个未知的bean作用域。

Initial web configuration

To support the scoping of beans at the request, session, application, and websocket levels (web-scoped beans), some minor initial configuration is required before you define your beans. (This initial setup is not required for the standard scopes, singleton and prototype.)

为了支持requestsessionapplicationwebsocket标准的bean作用域,在你定义你的bean之前需要进行一些较小的初始化配置。(对于标准作用域singletonprototype,初始化设置不需要的。)

How you accomplish this initial setup depends on your particular Servlet environment.

怎样完成这个初始化设置依赖于你特定的Servlet环境。

If you access scoped beans within Spring Web MVC, in effect, within a request that is processed by the Spring DispatcherServlet, then no special setup is necessary: DispatcherServlet already exposes all relevant state.

如果你在Spring Web MVC中访问具有作用域的beans,请求内部是通过Spring的DispatcherServlet来处理的,不需要特定设置:DispatcherServlet已经显示了所有相关的状态。

If you use a Servlet 2.5 web container, with requests processed outside of Spring’s DispatcherServlet (for example, when using JSF or Struts), you need to register the org.springframework.web.context.request.RequestContextListener ServletRequestListener. For Servlet 3.0+, this can be done programmatically via the WebApplicationInitializer interface. Alternatively, or for older containers, add the following declaration to your web application’s web.xml file:

如果你使用Servlet 2.5的web容器,在Spring的DispatcherServlet之外处理请求(例如使用JSF或Struts时),你需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener。对于Servlet 3.0+,能通过WebApplicationInitializer接口以编程方式处理。对于更早的容器,可以在应用程序的web.xml文件中添加下面的声明来代替:

1
2
3
4
5
6
7
8
9
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>

Alternatively, if there are issues with your listener setup, consider using Spring’s RequestContextFilter. The filter mapping depends on the surrounding web application configuration, so you have to change it as appropriate.

如果你的监听器设置有问题,作为一种选择,你可以考虑Spring的RequestContextFilter。过滤器映射依赖于web应用程序的相关配置,因此你必须适当的更改它。

1
2
3
4
5
6
7
8
9
10
11
12
<web-app>
...
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>

DispatcherServlet, RequestContextListener, and RequestContextFilter all do exactly the same thing, namely bind the HTTP request object to the Thread that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.

DispatcherServletRequestContextListenerRequestContextFilter都是在做同样的事,也就是说将HTTP请求对象绑定到服务请求的Thread上。这使得request作用域和session作用域的beans在更深一层的调用链中是可用的。

Request scope

Consider the following XML configuration for a bean definition:

考虑下面的bean定义的XML配置:

1
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

The Spring container creates a new instance of the LoginAction bean by using the loginAction bean definition for each and every HTTP request. That is, the loginAction bean is scoped at the HTTP request level. You can change the internal state of the instance that is created as much as you want, because other instances created from the same loginAction bean definition will not see these changes in state; they are particular to an individual request. When the request completes processing, the bean that is scoped to the request is discarded.

对于每一个HTTP请求,Spring容器通过使用loginAction定义创建一个新的LoginAction bean实例。也就是说,loginAction bean的作用域是在HTTP request级别的。你可以任意改变创建的实例的内部状态,因为其它的根据loginAction bean定义创建的实例不会看到这些状态的改变;它们对于每个单独的请求都是独有的。当请求处理完成时,请求作用域的bean被销毁。

When using annotation-driven components or Java Config, the @RequestScope annotation can be used to assign a component to the request scope.

当使用注解驱动的组件或Java配置时,@RequestScope注解能用来指定一个组件的作用域为request。

1
2
3
4
5
@RequestScope
@Component
public class LoginAction {
// ...
}

Session scope

Consider the following XML configuration for a bean definition:

考虑下面的bean定义的XML配置:

1
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

The Spring container creates a new instance of the UserPreferences bean by using the userPreferences bean definition for the lifetime of a single HTTP Session. In other words, the userPreferences bean is effectively scoped at the HTTP Session level. As with request-scoped beans, you can change the internal state of the instance that is created as much as you want, knowing that other HTTP Session instances that are also using instances created from the same userPreferences bean definition do not see these changes in state, because they are particular to an individual HTTP Session. When the HTTP Session is eventually discarded, the bean that is scoped to that particular HTTP Session is also discarded.

对于单个HTTP Session的生命周期,Spring容器通过userPreferences bean定义创建一个UserPreferences bean实例。换句话说,userPreferences bean的有效作用域是HTTP Session级别的。正如request作用域的beans一样,你可以任意改变你想改变的创建的bean实例的内部状态,知道其它的使用根据userPreferences bean定义创建的HTTP Session实例也不会看到这些内部状态的改变,因为它们对于每个单独的HTTP Session都是独有的。当HTTP Session被最终销毁时,Session作用域的bean也被销毁。

When using annotation-driven components or Java Config, the @SessionScope annotation can be used to assign a component to the session scope.

当使用注解驱动的组件或Java配置时,@SessionScope注解能用来指定一个组件的作用域为session。

1
2
3
4
5
@SessionScope
@Component
public class UserPreferences {
// ...
}

Application scope

Consider the following XML configuration for a bean definition:

考虑下面的bean定义的XML配置:

1
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

The Spring container creates a new instance of the AppPreferences bean by using the appPreferences bean definition once for the entire web application. That is, the appPreferences bean is scoped at the ServletContext level, stored as a regular ServletContext attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per ServletContext, not per Spring ‘ApplicationContext’ (for which there may be several in any given web application), and it is actually exposed and therefore visible as a ServletContext attribute.

对于整个web应用而言,Spring容器根据appPreferences bean定义只创建一次AppPreferences bean的新实例。也就是说,appPreferences bean的作用域是ServletContext级别的,作为一个正规的ServletContext特性来存储。这有点类似于Spring的单例bean,但在两个方面是不同的:它对于每个ServletContext是单例的,而不是每个Spring ApplicationContext(在任何给定的web应用中可能有几个ApplicationContext),它是真正显露的,因此作为一个ServletContext特性是可见的。

When using annotation-driven components or Java Config, the @ApplicationScope annotation can be used to assign a component to the application scope.

当使用注解驱动的组件或Java配置时,@ApplicationScope注解能用来指定一个组件的作用域为Application。

1
2
3
4
5
@ApplicationScope
@Component
public class AppPreferences {
// ...
}

Scoped beans as dependencies

The Spring IoC container manages not only the instantiation of your objects (beans), but also the wiring up of collaborators (or dependencies). If you want to inject (for example) an HTTP request scoped bean into another bean of a longer-lived scope, you may choose to inject an AOP proxy in place of the scoped bean. That is, you need to inject a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real target object from the relevant scope (such as an HTTP request) and delegate method calls onto the real object.

Spring IoC容器不仅管理对象的实例化,而且管理协作者(或依赖)的绑定。例如,如果你想将一个具有HTTP request作用域的bean注入到另一个具有更长生命周期作用域的bean中,你可以选择注入一个AOP代理来代替具有作用域的bean。也就是说,你需要注入一个代理对象,这个对象能显露与具有作用域的对象相同的接口,但也能从相关的作用域中(例如HTTP request作用域)得到真正的目标对象,能通过委派方法调用到真正的对象。

You may also use <aop:scoped-proxy/> between beans that are scoped as singleton, with the reference then going through an intermediate proxy that is serializable and therefore able to re-obtain the target singleton bean on deserialization.

When declaring <aop:scoped-proxy/> against a bean of scope prototype, every method call on the shared proxy will lead to the creation of a new target instance which the call is then being forwarded to.

Also, scoped proxies are not the only way to access beans from shorter scopes in a lifecycle-safe fashion. You may also simply declare your injection point (i.e. the constructor/setter argument or autowired field) as ObjectFactory<MyTargetBean>, allowing for a getObject() call to retrieve the current instance on demand every time it is needed - without holding on to the instance or storing it separately.

The JSR-330 variant of this is called Provider, used with a Provider<MyTargetBean> declaration and a corresponding get() call for every retrieval attempt. See here for more details on JSR-330 overall.

 

你也可以在作用域为singleton的beans之间使用<aop:scoped-proxy/>,将通过中间代理的引用进行序列化,因此能通过反序列化重新获得目标的单例bean。

当将作用域为prototype的bean声明为<aop:scoped-proxy/>时,每个在共享代理上的方法调用会引起一个新目标实例(调用朝向的)的创建。

通过生命周期安全的方式访问更短的作用域中beans,作用域代理也不是唯一的方式。你也可以简单的声明你的注入点(例如,构造函数/setter参数或自动装配领域)为ObjectFactory<MyTargetBean>,考虑到每次需要的时候通过getObject()调用来取得索要的当前实例——没有分别控制实例或储存它。

JSR-300变量被称作Provider,对于每一次取回尝试使用Provider<MyTargetBean>声明和对应的get()调用。关于JSR-330整体的更多细节请看这儿

The configuration in the following example is only one line, but it is important to understand the “why” as well as the “how” behind it.

下面例子中的配置只有一行,但对于理解它背后的”why”和”how”是重要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- instructs the container to proxy the surrounding bean -->
<aop:scoped-proxy/>
</bean>

<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userService" class="com.foo.SimpleUserService">
<!-- a reference to the proxied userPreferences bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>

To create such a proxy, you insert a child <aop:scoped-proxy/> element into a scoped bean definition (see the section called “Choosing the type of proxy to create” and Chapter 38, XML Schema-based configuration). Why do definitions of beans scoped at the request, session and custom-scope levels require the <aop:scoped-proxy/> element? Let’s examine the following singleton bean definition and contrast it with what you need to define for the aforementioned scopes (note that the following userPreferences bean definition as it stands is incomplete).

为了创建这样一个代理,你插入一个子元素<aop:scoped-proxy/>到具有作用域的bean定义中(看”选择创建的代理类型”小节和38章,基于XML Schema的配置)。为什么bean定义的作用域为requestsession和定制作用域级别需要<aop:scoped-proxy/>元素?让我们检查下面的单例bean定义,并将它与你需要定义的前面提到的作用域进行比较(注意下面的userPreferences bean定义按目前情况是不完全的)。

1
2
3
4
5
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>

In the preceding example, the singleton bean userManager is injected with a reference to the HTTP Session-scoped bean userPreferences. The salient point here is that the userManager bean is a singleton: it will be instantiated exactly once per container, and its dependencies (in this case only one, the userPreferences bean) are also injected only once. This means that the userManager bean will only operate on the exact same userPreferences object, that is, the one that it was originally injected with.

在上面的例子中,单例bean userManager通过引用被注入到具有HTTP Session作用域的bean userPreferences中。这的突出点是userManager bean是单例:每个容器它将确定的被实例化一次,它的依赖(在这个例子中只有一个,userPreferences bean)也只注入一次。这意味着userManager bean只能对确定的同一个userPreferences对象进行操作,也就是最初注入的那个对象。

This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean, for example injecting an HTTP Session-scoped collaborating bean as a dependency into singleton bean. Rather, you need a single userManager object, and for the lifetime of an HTTP Session, you need a userPreferences object that is specific to said HTTP Session. Thus the container creates an object that exposes the exact same public interface as the UserPreferences class (ideally an object that is a UserPreferences instance) which can fetch the real UserPreferences object from the scoping mechanism (HTTP request, Session, etc.). The container injects this proxy object into the userManager bean, which is unaware that this UserPreferences reference is a proxy. In this example, when a UserManager instance invokes a method on the dependency-injected UserPreferences object, it actually is invoking a method on the proxy. The proxy then fetches the real UserPreferences object from (in this case) the HTTP Session, and delegates the method invocation onto the retrieved real UserPreferences object.

当将一个短期作用域的bean注入到一个长期作用域的bean中时,这不是你想要的行为,例如将一个具有HTTP Session作用域的协作bean作为一个依赖注入到一个单例bean中。当然,你需要一个单一的userManager对象,对于HTTP Session的生命周期,你需要一个特定的被称为HTTP SessionuserPreferences对象。因此容器创建了一个与UserPreferences类暴露相同的公共接口的对象(理想情况下是一个UserPreferences实例),这个对象能从作用域机制中(HTTP request,Session等)取得真正的UserPreferences对象。容器将这个代理对象注入到userManager bean中,userManager bean不会意识到UserPreferences引用是一个代理。在这个例子中,当UserManager实例调用依赖注入的UserPreferences对象的方法时,它实际上调用的是代理中的一个方法。代理能从HTTP Session中(在这个例子)取得真正的UserPreferences对象,将方法调用委托到取得的真正的UserPreferences对象上。

Thus you need the following, correct and complete, configuration when injecting request- and session-scoped beans into collaborating objects:

因此当注入具有request或session作用域的bean到协作对象中时,你需要下面的,正确的,完整的配置:

1
2
3
4
5
6
7
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>

Choosing the type of proxy to create

By default, when the Spring container creates a proxy for a bean that is marked up with the <aop:scoped-proxy/> element, a CGLIB-based class proxy is created.

当Spring容器为具有<aop:scoped-proxy/>标记的bean创建代理时,默认情况下,创建一个基于CGLIB的类代理。

CGLIB proxies only intercept public method calls! Do not call non-public methods on such a proxy; they will not be delegated to the actual scoped target object.

 

CGLIB代理只拦截公有方法调用。在这个代理上不调用非公有方法;它们不能委托给实际作用域目标对象。

Alternatively, you can configure the Spring container to create standard JDK interface-based proxies for such scoped beans, by specifying false for the value of the proxy-target-class attribute of the <aop:scoped-proxy/> element. Using JDK interface-based proxies means that you do not need additional libraries in your application classpath to effect such proxying. However, it also means that the class of the scoped bean must implement at least one interface, and that all collaborators into which the scoped bean is injected must reference the bean through one of its interfaces.

作为一种选择,对于这种具有作用域的bean你可以配置Spring容器创建标准JDK基于接口的代理,通过指定<aop:scoped-proxy/>元素的proxy-target-class特定的值为false。使用JDK基于接口的代理意味着在你应用程序类路径中你不需要额外的库来支持这种代理的使用。然而,它也意味着具有作用域的bean的类必须实现至少一个接口,并且注入这个bean的所有协作者必须通过它接口中的一个来引用它。

1
2
3
4
5
6
7
8
<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>

For more detailed information about choosing class-based or interface-based proxying, see Section 7.6, “Proxying mechanisms”.

关于选择基于类或基于接口代理的更多细节信心,请看7.6小节,”代理机制”。

3.5.5 Custom scopes

The bean scoping mechanism is extensible; You can define your own scopes, or even redefine existing scopes, although the latter is considered bad practice and you cannot override the built-in singleton and prototype scopes.

bean作用域机制是可扩展的;你可以定义你自己的作用域,甚至重新定义现有的作用域,虽然后者被认为是一种不好的实践,你不能覆盖内置的singleton作用域和prototype作用域。

Creating a custom scope

To integrate your custom scope(s) into the Spring container, you need to implement the org.springframework.beans.factory.config.Scope interface, which is described in this section. For an idea of how to implement your own scopes, see the Scope implementations that are supplied with the Spring Framework itself and the Scope javadocs, which explains the methods you need to implement in more detail.

为了将你的定制作用域集成到Spring容器中,你需要实现org.springframework.beans.factory.config.Scope接口,这一节将描述这个接口。对于怎样实现你自己作用域的想法,请看Spring框架本身提供的Scope实现和Scope文档,它们解释了你需要实现的方法的更多细节。

The Scope interface has four methods to get objects from the scope, remove them from the scope, and allow them to be destroyed.

Scope接口有四个方法,从作用域中取得对象,从作用域中移除对象,并且允许它们被销毁。

The following method returns the object from the underlying scope. The session scope implementation, for example, returns the session-scoped bean (and if it does not exist, the method returns a new instance of the bean, after having bound it to the session for future reference).

下面的方法从潜在的作用域返回对象。以session作用域实现为例,返回具有session作用域的bean(如果它不存在,这个方法返回一个bean的新实例,然后绑定到session中准备将来引用)。

1
Object get(String name, ObjectFactory objectFactory)

The following method removes the object from the underlying scope. The session scope implementation for example, removes the session-scoped bean from the underlying session. The object should be returned, but you can return null if the object with the specified name is not found.

下面的方法从潜在作用域中移除对象。以session作用域实现为例,从潜在的session中移除session作用域的bean。对象应该被返回,但如果没有找到指定名字的对象会返回空。

1
Object remove(String name)

The following method registers the callbacks the scope should execute when it is destroyed or when the specified object in the scope is destroyed. Refer to the javadocs or a Spring scope implementation for more information on destruction callbacks.

下面的方法是注册当作用域销毁时或当作用域中的指定对象销毁时,作用域应该执行的回调函数。销毁回调函数的更多信息请看文档或Spring作用域实现。

1
void registerDestructionCallback(String name, Runnable destructionCallback)

The following method obtains the conversation identifier for the underlying scope. This identifier is different for each scope. For a session scoped implementation, this identifier can be the session identifier.

下面的方法是获得潜在作用域的会话标识符。每个作用域的标识符都是不同的。对于session作用域实现,标识符是session标识符。

1
String getConversationId()

Using a custom scope

After you write and test one or more custom Scope implementations, you need to make the Spring container aware of your new scope(s). The following method is the central method to register a new Scope with the Spring container:

在你编写和测试一个或多个定制Scope实现之后,你需要让Spring容器感知到你的新作用域。下面是在Spring容器中注册一个新Scope的主要方法:

1
void registerScope(String scopeName, Scope scope);

This method is declared on the ConfigurableBeanFactory interface, which is available on most of the concrete ApplicationContext implementations that ship with Spring via the BeanFactory property.

这个方法是在ConfigurableBeanFactory接口中声明的,在大多数具体的ApplicationContext实现中都可获得,在Spring中通过BeanFactory属性得到。

The first argument to the registerScope(..) method is the unique name associated with a scope; examples of such names in the Spring container itself are singleton and prototype. The second argument to the registerScope(..) method is an actual instance of the custom Scope implementation that you wish to register and use.

registerScope(..)方法中的第一个参数是关于作用域的唯一名字;Spring容器本身中的这种名字的例子是singletonprototyperegisterScope(..)方法中的第二个参数是你想注册和使用的定制Scope实现的真正实例。

Suppose that you write your custom Scope implementation, and then register it as below.

假设你编写了你的定制Scope实现并按如下注册。

The example below uses SimpleThreadScope which is included with Spring, but not registered by default. The instructions would be the same for your own custom Scope implementations.

 

下面的例子使用Spring包含的SimpleThreadScope,但默认是不注册的。这个用法说明与你自己的定制Scope是一样的。

1
2
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

You then create bean definitions that adhere to the scoping rules of your custom Scope:

然后创建具有你自己定制的Scope规则的bean定义:

1
<bean id="..." class="..." scope="thread">

With a custom Scope implementation, you are not limited to programmatic registration of the scope. You can also do the Scope registration declaratively, using the CustomScopeConfigurer class:

在定制Scope实现后,你不会受限于作用域的程序注册。你也可以声明式的进行Scope注册,使用CustomScopeConfigurer类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>

<bean id="bar" class="x.y.Bar" scope="thread">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean>

<bean id="foo" class="x.y.Foo">
<property name="bar" ref="bar"/>
</bean>

</beans>

When you place <aop:scoped-proxy/> in a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().

 

当你在FactoryBean实现中放入<aop:scoped-proxy/>时,它是工厂bean本身具有作用域,不是从getObject()中返回的对象。

3.6 Customizing the nature of a bean

3.6.1 Lifecycle callbacks

To interact with the container’s management of the bean lifecycle, you can implement the Spring InitializingBean and DisposableBean interfaces. The container calls afterPropertiesSet() for the former and destroy() for the latter to allow the bean to perform certain actions upon initialization and destruction of your beans.

为了与容器中bean生命周期的管理进行交互,你可以实现Spring的InitializingBeanDisposableBean接口。当初始化beans时容器会调用InitializingBean中的afterPropertiesSet()方法,当销毁beans时容器会调用DisposableBean中的destroy()方法,在这两个方法中bean可以执行特定的行为。

The JSR-250 @PostConstruct and @PreDestroy annotations are generally considered best practice for receiving lifecycle callbacks in a modern Spring application. Using these annotations means that your beans are not coupled to Spring specific interfaces. For details see Section 3.9.8, “@PostConstruct and @PreDestroy”.

If you don’t want to use the JSR-250 annotations but you are still looking to remove coupling consider the use of init-method and destroy-method object definition metadata.

 

在现代Spring应用中,通常认为JSR-250的@PostConstruct@PreDestroy注解是最佳实践接收生命周期回调函数的方法。使用这些注解意味着你的bean没有耦合Spring特定的接口。更多细节请看3.9.8小节,”@PostConstruct和@PreDestroy”。

如果你不想使用JSR-250注解,但你仍要注意解耦,可以考虑使用对象定义元数据中的初始化方法和方法。

Internally, the Spring Framework uses BeanPostProcessor implementations to process any callback interfaces it can find and call the appropriate methods. If you need custom features or other lifecycle behavior Spring does not offer out-of-the-box, you can implement a BeanPostProcessoryourself. For more information, see Section 3.8, “Container Extension Points”.

在Spring内部,Spring框架使用BeanPostProcessor实现来处理任何它能发现的回调接口并调用合适的方法。如果你需要定制Spring不能提供的开箱即用的功能或其它生命周期行为,你可以自己实现BeanPostProcessor。更多信息请看3.8小节,”容器扩展点”。

In addition to the initialization and destruction callbacks, Spring-managed objects may also implement the Lifecycle interface so that those objects can participate in the startup and shutdown process as driven by the container’s own lifecycle.

除了初始化回调函数和析构回调函数之外,Spring管理的对象也可以实现Lifecycle接口,这些对象可以参与容器自身生命周期驱动的启动和关闭过程。

The lifecycle callback interfaces are described in this section.

本节描述了生命周期回调接口。

Initialization callbacks

The org.springframework.beans.factory.InitializingBean interface allows a bean to perform initialization work after all necessary properties on the bean have been set by the container. The InitializingBean interface specifies a single method:

org.springframework.beans.factory.InitializingBean接口在容器设置了bean所有的必须属性之后,允许bean执行初始化工作。InitializingBean接口指定了一个方法:

1
void afterPropertiesSet() throws Exception;

It is recommended that you do not use the InitializingBean interface because it unnecessarily couples the code to Spring. Alternatively, use the @PostConstruct annotation or specify a POJO initialization method. In the case of XML-based configuration metadata, you use the init-method attribute to specify the name of the method that has a void no-argument signature. With Java config, you use the initMethod attribute of @Bean, see the section called “Receiving lifecycle callbacks”. For example, the following:

建议你不使用InitializingBean接口,因为它对代码与Spring进行了不必要的耦合。作为一种替代方法,你可以使用@PostConstruct注解或指定一个POPJO的初始化方法。在基于XML配置元数据的情况下,你可以使用init-method特性来指定方法的名称,方法是没有返回值和参数的。如果使用Java配置,你可以使用@BeaninitMethod特性,请看”接收生命周期回调”小节。例如,下面的代码:

1
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
1
2
3
4
5
6
7
public class ExampleBean {

public void init() {
// do some initialization work
}

}

…​is exactly the same as…​

等价于:

1
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
1
2
3
4
5
6
7
public class AnotherExampleBean implements InitializingBean {

public void afterPropertiesSet() {
// do some initialization work
}

}

but does not couple the code to Spring.

但没有与Spring代码耦合。

Destruction callbacks

Implementing the org.springframework.beans.factory.DisposableBean interface allows a bean to get a callback when the container containing it is destroyed. The DisposableBean interface specifies a single method:

实现org.springframework.beans.factory.DisposableBean接口允许容器包含的bean销毁时调用回调函数。DisposableBean接口指定了一个方法:

1
void destroy() throws Exception;

It is recommended that you do not use the DisposableBean callback interface because it unnecessarily couples the code to Spring. Alternatively, use the @PreDestroy annotation or specify a generic method that is supported by bean definitions. With XML-based configuration metadata, you use the destroy-method attribute on the <bean/>. With Java config, you use the destroyMethod attribute of @Bean, see the section called “Receiving lifecycle callbacks”. For example, the following definition:

建议你不使用DisposableBean回调接口,因为它对代码与Spring进行了不必要的耦合。作为一种替代方法,你可以使用@PreDestroy注解或指定一个bean定义支持的通用方法。在基于XML配置元数据的情况下,你可以使用<bean/>destroy-method特性。如果使用Java配置,你可以使用@BeandestroyMethod特性,请看”接收生命周期回调”小节。例如,下面的定义:

1
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
1
2
3
4
5
6
7
public class ExampleBean {

public void cleanup() {
// do some destruction work (like releasing pooled connections)
}

}

is exactly the same as:

等价于:

1
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
1
2
3
4
5
6
7
public class AnotherExampleBean implements DisposableBean {

public void destroy() {
// do some destruction work (like releasing pooled connections)
}

}

but does not couple the code to Spring.

但没有与Spring代码耦合。

The destroy-method attribute of a <bean> element can be assigned a special (inferred) value which instructs Spring to automatically detect a public close or shutdown method on the specific bean class (any class that implements java.lang.AutoCloseable or java.io.Closeable would therefore match). This special (inferred) value can also be set on the default-destroy-method attribute of a <beans> element to apply this behavior to an entire set of beans (see the section called “Default initialization and destroy methods”). Note that this is the default behavior with Java config.

 

<bean>元素的destroy-method特性可以分配一个特殊值(inferred),它会指导Spring自动检测指定的bean类的公有close方法或shutdown方法(可以匹配任何实现java.lang.AutoCloseablejava.io.Closeable的类)。为了将这种行为应用到beans的全部集合中,特殊值(inferred)可以设置在<beans>元素中的default-destroy-method特性上(请看”默认初始化方法和销毁方法”小节)。注意Java配置的默认行为。

Default initialization and destroy methods

When you write initialization and destroy method callbacks that do not use the Spring-specific InitializingBean and DisposableBean callback interfaces, you typically write methods with names such as init(), initialize(), dispose(), and so on. Ideally, the names of such lifecycle callback methods are standardized across a project so that all developers use the same method names and ensure consistency.

当你编写初始化回调函数和析构回调函数时,不要使用Spring特定的InitializingBeanDisposableBean回调接口,自己编写方法,方法名通常为init()initialize()dispose()等等。理想情况下,这种生命周期回调方法的名称在整个工程中是标准化的,以便所有开发人员使用同样的方法名称,保证一致性。

You can configure the Spring container to look for named initialization and destroy callback method names on every bean. This means that you, as an application developer, can write your application classes and use an initialization callback called init(), without having to configure an init-method="init" attribute with each bean definition. The Spring IoC container calls that method when the bean is created (and in accordance with the standard lifecycle callback contract described previously). This feature also enforces a consistent naming convention for initialization and destroy method callbacks.

你可以配置Spring容器查找每个bean的初始化方法和析构方法时的名字。这意味着,作为一个应用开发者,你可以编写应用程序类并使用名为init()的初始化回调方法,而不必在每个bean定义中配置init-method="init"特性。当bean创建时,Spring Ioc容器调用这个方法(按照前面描述的标准生命周期回调约定)。这个功能也强制了初始化方法和析构方法命名规范的一致性。

Suppose that your initialization callback methods are named init() and destroy callback methods are named destroy(). Your class will resemble the class in the following example.

假设你的初始化回调方法名为init(),析构回调方法名为destroy()。你的类应该与下面例子中的类类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class DefaultBlogService implements BlogService {

private BlogDao blogDao;

public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}

// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
1
2
3
4
5
6
7
<beans default-init-method="init">

<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>

</beans>

The presence of the default-init-method attribute on the top-level <beans/> element attribute causes the Spring IoC container to recognize a method called init on beans as the initialization method callback. When a bean is created and assembled, if the bean class has such a method, it is invoked at the appropriate time.

位于顶层<beans/>元素中的default-init-method特性,会让Spring IoC容器将beans中的名为init的方法识别为初始化回调方法。当一个bean创建和组装时,如果bean类有这样一个方法,它会在恰当的时间被调用。

You configure destroy method callbacks similarly (in XML, that is) by using the default-destroy-method attribute on the top-level <beans/> element.

类似的,你可以在顶层元素<beans/>中设置default-destroy-method特性来配置析构回调方法的名字。

Where existing bean classes already have callback methods that are named at variance with the convention, you can override the default by specifying (in XML, that is) the method name using the init-method and destroy-method attributes of the <bean/> itself.

在现有的bean类已经有不符合命名规范的回调方法的情况下,你也可以通过在<bean/>本身的init-methoddestroy-method特性(在XML中)中指定方法名称来覆盖<beans/>中的默认名称。

The Spring container guarantees that a configured initialization callback is called immediately after a bean is supplied with all dependencies. Thus the initialization callback is called on the raw bean reference, which means that AOP interceptors and so forth are not yet applied to the bean. A target bean is fully created first, then an AOP proxy (for example) with its interceptor chain is applied. If the target bean and the proxy are defined separately, your code can even interact with the raw target bean, bypassing the proxy. Hence, it would be inconsistent to apply the interceptors to the init method, because doing so would couple the lifecycle of the target bean with its proxy/interceptors and leave strange semantics when your code interacts directly to the raw target bean.

在bean被提供了所有依赖之后,Spring容器确保会立刻调用配置的初始化回调方法。因此初始化回调会在原生bean引用上调用,这意味着AOP拦截器等仍不能应用到bean中。首先要完整的创建目标bean,然后才会应用AOP代理(例如)等拦截器链。如果分别定义了目标bean和代理,你的代码甚至能绕过代理直接与原生的目标bean进行交互。将拦截器应用到初始化方法上可能会产生不一致性,因为这样做会使目标bean的生命周期与它的代理/拦截器相耦合,当你的代码与原生目标bean直接进行交互时,语义会变的很奇怪。

Combining lifecycle mechanisms

As of Spring 2.5, you have three options for controlling bean lifecycle behavior: the InitializingBean and DisposableBean callback interfaces; custom init() and destroy() methods; and the @PostConstruct and @PreDestroy annotations. You can combine these mechanisms to control a given bean.

从Spring 2.5开始,在控制bean的生命周期行为时,你有三中选择:InitializingBeanDisposableBean回调接口;定制init()destroy()方法;@PostConstruct@PreDestroy`注解。在控制一个给定bean时你可以组合这些机制。

If multiple lifecycle mechanisms are configured for a bean, and each mechanism is configured with a different method name, then each configured method is executed in the order listed below. However, if the same method name is configured - for example, init() for an initialization method - for more than one of these lifecycle mechanisms, that method is executed once, as explained in the preceding section.

 

如果一个bean配置了多生命周期机制,每种机制配置了一个不同的方法名,那么每一个配置的方法会按照下面的顺序列表来执行。但是如果配置了相同的名字——例如,init()初始化方法——不止在一个生命周期机制中配置,那么这个方法只能执行一次,像之前所说的那样。

Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:

  • Methods annotated with @PostConstruct

  • afterPropertiesSet() as defined by the InitializingBean callback interface

  • A custom configured init() method

同一个bean配置了多生命周期机制,并有不同的初始化方法,那么调用顺序如下:

  • 先调用有注解@PostConstruct的方法

  • 然后调用InitializingBean回调接口定义的afterPropertiesSet()方法

  • 最好调用定制配置的init()方法

Destroy methods are called in the same order:

  • Methods annotated with @PreDestroy

  • destroy() as defined by the DisposableBean callback interface

  • A custom configured destroy() method

析构方法按同样的顺序调用:

  • 先调用有@PreDestroy注解的方法

  • 再调用DisposableBean回调接口定义的destroy()方法

  • 最好调用定制配置的destroy()方法

Startup and shutdown callbacks

The Lifecycle interface defines the essential methods for any object that has its own lifecycle requirements (e.g. starts and stops some background process):

Lifecycle接口定义了任何对象生命周期都需要的基本方法(例如启动和停止一些背景处理):

1
2
3
4
5
6
7
8
9
public interface Lifecycle {

void start();

void stop();

boolean isRunning();

}

Any Spring-managed object may implement that interface. Then, when the ApplicationContext itself receives start and stop signals, e.g. for a stop/restart scenario at runtime, it will cascade those calls to all Lifecycle implementations defined within that context. It does this by delegating to a LifecycleProcessor:

任何Spring管理的对象都可以实现那个接口。当ApplicationContext本身收到启动启动和关闭信号时,例如运行时关闭/再启动场景,它将级联调用所有的上下文定义的Lifecycle实现。它通过委托LifecycleProcessor来完成这个功能:

1
2
3
4
5
6
7
public interface LifecycleProcessor extends Lifecycle {

void onRefresh();

void onClose();

}

Notice that the LifecycleProcessor is itself an extension of the Lifecycle interface. It also adds two other methods for reacting to the context being refreshed and closed.

注意LifecycleProcessor本身是Lifecycle接口的一个扩展。它也添加了两个其它的方法来响应上下文的再刷新和关闭的。

Note that the regular org.springframework.context.Lifecycle interface is just a plain contract for explicit start/stop notifications and does NOT imply auto-startup at context refresh time. Consider implementing org.springframework.context.SmartLifecycle instead for fine-grained control over auto-startup of a specific bean (including startup phases). Also, please note that stop notifications are not guaranteed to come before destruction: On regular shutdown, all Lifecycle beans will first receive a stop notification before the general destruction callbacks are being propagated; however, on hot refresh during a context’s lifetime or on aborted refresh attempts, only destroy methods will be called.

注意正规的org.springframework.context.Lifecycle接口只是一个显式启动/关闭通知的协议,并不意味着在上下文刷新时自动启动。考虑实现org.springframework.context.SmartLifecycle接口来实现对指定bean自动启动的细粒度控制(包括启动时期)。请注意停止通知不能保证在销毁之前到来:在正式关闭时,所有的Lifecycle beans在通常的析构回调传播之前首先会收到停止通知;但是,在上下文使用期间进行热刷新或尝试取消再刷新,只会调用析构方法。

The order of startup and shutdown invocations can be important. If a “depends-on” relationship exists between any two objects, the dependent side will start after its dependency, and it will stop before its dependency. However, at times the direct dependencies are unknown. You may only know that objects of a certain type should start prior to objects of another type. In those cases, the SmartLifecycle interface defines another option, namely the getPhase() method as defined on its super-interface, Phased.

启动和关闭的调用顺序是很重要的。如果任何两个对象间存在一个”depends-on”关系,那么依赖关系将在它的依赖之后开始,在它的依赖之前停止。然而有时直接的依赖关系是未知的。你可能只知道某个类型的对象应该在另一个类型的对象之前启动。在那种情况下,SmartLifecycle接口定义了另一种选择,也就是说getPhase()定义在它的父接口Phased中。

1
2
3
public interface Phased {
int getPhase();
}
1
2
3
4
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}

When starting, the objects with the lowest phase start first, and when stopping, the reverse order is followed. Therefore, an object that implements SmartLifecycle and whose getPhase() method returns Integer.MIN_VALUE would be among the first to start and the last to stop. At the other end of the spectrum, a phase value of Integer.MAX_VALUE would indicate that the object should be started last and stopped first (likely because it depends on other processes to be running). When considering the phase value, it’s also important to know that the default phase for any “normal” Lifecycle object that does not implement SmartLifecycle would be 0. Therefore, any negative phase value would indicate that an object should start before those standard components (and stop after them), and vice versa for any positive phase value.

当开始时,最低相位的对象先启动,当停止时,最高相位的对象先停止。因此,实现了SmartLifecycle接口,getPhase()方法返回值为Integer.MIN_VALUE的对象将最先启动并最后停止。另一方面,相位值Integer.MAX_VALUE表明对象应该最后启动,最先停止(可能是因为它依赖其它运行的进程)。当考虑相位值时,知道任何没有实现SmartLifecycle接口的Lifecycle对象的默认值为0是很重要的。因此,任何负相位值表示对象应该在那么标准组件之前启动(在它们之后停止),反之为任何正相位值。

As you can see the stop method defined by SmartLifecycle accepts a callback. Any implementation must invoke that callback’s run() method after that implementation’s shutdown process is complete. That enables asynchronous shutdown where necessary since the default implementation of the LifecycleProcessor interface, DefaultLifecycleProcessor, will wait up to its timeout value for the group of objects within each phase to invoke that callback. The default per-phase timeout is 30 seconds. You can override the default lifecycle processor instance by defining a bean named “lifecycleProcessor” within the context. If you only want to modify the timeout, then defining the following would be sufficient:

正如你看到的,在SmartLifecycle中定义的停止方法接收一个回调函数。任何实现在关闭进程完成之后都必须调用回调的run()方法。当需要时这可以进行异步关闭,因为LifecycleProcessor接口、DefaultLifecycleProcessor接口的默认实现会等待每个阶段的对象组直到达到超时值,然后调用回调函数。默认每个时期的超时值为30秒。你可以在上下文中通过定义名为”lifecycleProcessor”的bean来覆盖默认的生命周期处理器实例。如果你只想修改超时值,如下定义是足够的:

1
2
3
4
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

As mentioned, the LifecycleProcessor interface defines callback methods for the refreshing and closing of the context as well. The latter will simply drive the shutdown process as if stop() had been called explicitly, but it will happen when the context is closing. The refresh callback on the other hand enables another feature of SmartLifecycle beans. When the context is refreshed (after all objects have been instantiated and initialized), that callback will be invoked, and at that point the default lifecycle processor will check the boolean value returned by each SmartLifecycle object’s isAutoStartup() method. If “true”, then that object will be started at that point rather than waiting for an explicit invocation of the context’s or its own start() method (unlike the context refresh, the context start does not happen automatically for a standard context implementation). The “phase” value as well as any “depends-on” relationships will determine the startup order in the same way as described above.

像上面提到的那样,LifecycleProcessor接口为再刷新和上下文的关闭也定义了回调方法。后者会简单的驱动关闭进程就像显式的调用了stop()方法一样,但当上下文关闭时它才会发生。另一方面refresh回调能使SmartLifecycle beans的另一个功能可用。当上下文再刷新时(所有对象已经实例化并初始化),回调函数将被调用,那时默认的生命周期处理器将会检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果为true,对象将会在那时启动而不是等待上下文的显式调用或它自己的start()方法(不像上下文再刷新,对于一个标准的上下文实现上下启动不会自动发生)。”phase”值以及”depends-on”关系将决定启动顺序,像上面描述的一样。

Shutting down the Spring IoC container gracefully in non-web applications

This section applies only to non-web applications. Spring’s web-based ApplicationContext implementations already have code in place to shut down the Spring IoC container gracefully when the relevant web application is shut down.

 

这一节只应用于非web应用。Spring的基于web的ApplicationContext实现已经有代码来处理当相关的web应用关闭时,妥善关闭Spring IoC容器的问题。

If you are using Spring’s IoC container in a non-web application environment; for example, in a rich client desktop environment; you register a shutdown hook with the JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your singleton beans so that all resources are released. Of course, you must still configure and implement these destroy callbacks correctly.

如果你在非web应用环境使用Spring的IoC容器;例如,在一个富桌面客户端环境中,你在JVM中注册一个关闭钩子。这样做确保了妥善的关闭,为了释放所有资源需要调用与单例beans相关的析构方法。当然,你仍然必须正确的配置和实现这些销毁回调函数。

To register a shutdown hook, you call the registerShutdownHook() method that is declared on the ConfigurableApplicationContext interface:

为了注册一个关闭钩子,你可以调用ConfigurableApplicationContext接口中声明的registerShutdownHook()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

public static void main(final String[] args) throws Exception {

ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
new String []{"beans.xml"});

// add a shutdown hook for the above context...
ctx.registerShutdownHook();

// app runs here...

// main method exits, hook is called prior to the app shutting down...

}
}

3.6.2 ApplicationContextAware and BeanNameAware

When an ApplicationContext creates an object instance that implements the org.springframework.context.ApplicationContextAware interface, the instance is provided with a reference to that ApplicationContext.

ApplicationContext创建一个实现org.springframework.context.ApplicationContextAware接口的对象实例时,这个实例会提供一个ApplicationContext的引用。

1
2
3
4
5
public interface ApplicationContextAware {

void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

Thus beans can manipulate programmatically the ApplicationContext that created them, through the ApplicationContext interface, or by casting the reference to a known subclass of this interface, such as ConfigurableApplicationContext, which exposes additional functionality. One use would be the programmatic retrieval of other beans. Sometimes this capability is useful; however, in general you should avoid it, because it couples the code to Spring and does not follow the Inversion of Control style, where collaborators are provided to beans as properties. Other methods of the ApplicationContext provide access to file resources, publishing application events, and accessing a MessageSource. These additional features are described in Section 3.15, “Additional Capabilities of the ApplicationContext”.

因此beans可以以编程方式操纵创建它们的ApplicationContext,通过ApplicationContext接口,或通过将引用抛给这个接口的一个已知子类,例如ConfigurableApplicationContext,它暴露了额外的功能。一个方法是编程式检索其他的bean。有时这个能力是很有用的,但是通常你应该避免使用它,因为它耦合了代码和Spring,不能遵循控制反转的风格,在控制反转中协作者是作为属性提供给beans的。ApplicationContext的其它方法提供了对文件资源的访问,发布应用事件,访问MessageSource的功能。这些额外的特性将在3.15小节『ApplicationContext”的额外能力』中描述。

As of Spring 2.5, autowiring is another alternative to obtain reference to the ApplicationContext. The “traditional” constructor and byTypeautowiring modes (as described in Section 3.4.5, “Autowiring collaborators”) can provide a dependency of type ApplicationContext for a constructor argument or setter method parameter, respectively. For more flexibility, including the ability to autowire fields and multiple parameter methods, use the new annotation-based autowiring features. If you do, the ApplicationContext is autowired into a field, constructor argument, or method parameter that is expecting the ApplicationContext type if the field, constructor, or method in question carries the @Autowired annotation. For more information, see Section 3.9.2, “@Autowired”.

从Spring 2.5起,自动装配是另一种可替代的获得ApplicationContext引用的方法。『传统的』constructorbyType自动装配模式(如3.4.5小节所述,『自动装配协作者』)可以分别为构造函数参数或setter方法参数提供ApplicationContext类型的依赖。更多的灵活性包括自动装配变量的能力和多参数方法,使用新的基于注解的自动装配特性。如果你这一做的话,ApplicationContext可以被自动装配到变量中,构造函数参数中或方法参数中,如果讨论的变量,构造函数或方法有@Autowired注解,那么可以期望它是ApplicationContext类型。更多信息请看3.9.2小节,@autowired

When an ApplicationContext creates a class that implements the org.springframework.beans.factory.BeanNameAware interface, the class is provided with a reference to the name defined in its associated object definition.

ApplicationContext创建一个实现了org.springframework.beans.factory.BeanNameAware接口的类时,类中有相关的对象定义中定义的名称的引用。

1
2
3
4
5
public interface BeanNameAware {

void setBeanName(String name) throws BeansException;

}

The callback is invoked after population of normal bean properties but before an initialization callback such as InitializingBean afterPropertiesSet or a custom init-method.

在正常的bean属性填入之后,回调方法调用,但在初始化回调方法之前,例如InitializingBean的afterPropertiesSet或一个定制的初始化方法。

3.6.3 Other Aware interfaces

Besides ApplicationContextAware and BeanNameAware discussed above, Spring offers a range of Aware interfaces that allow beans to indicate to the container that they require a certain infrastructure dependency. The most important Aware interfaces are summarized below - as a general rule, the name is a good indication of the dependency type:

除了上面讨论的ApplicationContextAwareBeanNameAware之外,Spring给予了一系列Aware接口来允许beans向容器表明它们需要一个确定的基础结构依赖。最重要的Aware接口总结如下——作为一个通用规则,名字是依赖类型的一个很好暗示:

Table 3.4. Aware interfaces

Name Injected Dependency Explained in
ApplicationContextAware Declaring ApplicationContext Section 3.6.2, “ApplicationContextAware and BeanNameAware”
ApplicationEventPublisherAware Event publisher of the enclosing ApplicationContext Section 3.15, “Additional Capabilities of the ApplicationContext”
BeanClassLoaderAware Class loader used to load the bean classes. Section 3.3.2, “Instantiating beans”
BeanFactoryAware Declaring BeanFactory Section 3.6.2, “ApplicationContextAware and BeanNameAware”
BeanNameAware Name of the declaring bean Section 3.6.2, “ApplicationContextAware and BeanNameAware”
BootstrapContextAware Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContexts Chapter 28, JCA CCI
LoadTimeWeaverAware Defined weaver for processing class definition at load time Section 7.8.4, “Load-time weaving with AspectJ in the Spring Framework”
MessageSourceAware Configured strategy for resolving messages (with support for parametrization and internationalization) Section 3.15, “Additional Capabilities of the ApplicationContext”
NotificationPublisherAware Spring JMX notification publisher Section 27.7, “Notifications”
ResourceLoaderAware Configured loader for low-level access to resources Chapter 4, Resources
ServletConfigAware Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 18, Web MVC framework
ServletContextAware Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 18, Web MVC framework

表3.4. Aware接口

Name Injected Dependency Explained in
ApplicationContextAware 声明ApplicationContext Section 3.6.2, “ApplicationContextAware and BeanNameAware”
ApplicationEventPublisherAware 封装事件发布的ApplicationContext Section 3.15, “Additional Capabilities of the ApplicationContext”
BeanClassLoaderAware 用来加载bean的类加载器 Section 3.3.2, “Instantiating beans”
BeanFactoryAware 声明BeanFactory Section 3.6.2, “ApplicationContextAware and BeanNameAware”
BeanNameAware 声明的bean的名字 Section 3.6.2, “ApplicationContextAware and BeanNameAware”
BootstrapContextAware 容器运行的资源自适应BootstrapContext. 通常只在JCA aware ApplicationContexts可获得 Chapter 28, JCA CCI
LoadTimeWeaverAware 加载时为处理类定义定义的weaver Section 7.8.4, “Load-time weaving with AspectJ in the Spring Framework”
MessageSourceAware 解析消息配置策略 (支持参数化和国际化) Section 3.15, “Additional Capabilities of the ApplicationContext”
NotificationPublisherAware Spring JMX通知发布器 Section 27.7, “Notifications”
ResourceLoaderAware 为底层访问资源配置的加载器 Chapter 4, Resources
ServletConfigAware 容器运行的当前ServletConfig。 仅在web感知的Spring ApplicationContext中有效 Chapter 18, Web MVC framework
ServletContextAware 容器运行的当前ServletContext。 仅在web感知的Spring ApplicationContext中有效 Chapter 18, Web MVC framework

Note again that usage of these interfaces ties your code to the Spring API and does not follow the Inversion of Control style. As such, they are recommended for infrastructure beans that require programmatic access to the container.

注意这些接口的用法将你的代码与Spring进行了捆绑,不符合控制反转的风格。因此,它们是为那么需要以编程方式访问容器的基础结构beans推荐的。

3.7 Bean definition inheritance

A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values, or add others, as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.

Bean定义中可以包含许多配置信息,包括构造函数参数,属性值和容器的特定信息例如初始化方法,静态工厂方法名等等。子定义继承父定义的配置信息。子定义可以覆盖一些值,或按需要添加一些其它值。使用父子bean定义可以保存许多类型。实际上,这是一种模板形式。

If you work with an ApplicationContext interface programmatically, child bean definitions are represented by the ChildBeanDefinition class. Most users do not work with them on this level, instead configuring bean definitions declaratively in something like the ClassPathXmlApplicationContext. When you use XML-based configuration metadata, you indicate a child bean definition by using the parent attribute, specifying the parent bean as the value of this attribute.

如果你以编程方式使用ApplicationContext接口,那么子bean定义是通过ChildBeanDefinition定义来表示的。大多数用户不在这个层级上使用它们,而是在一些像ClassPathXmlApplicationContext中声明式的配置bean定义。当你使用基于XML的配置元数据时,你可以使用parent特性来指明一个子bean定义。在特性值中指定父bean。

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>

A child bean definition uses the bean class from the parent definition if none is specified, but can also override it. In the latter case, the child bean class must be compatible with the parent, that is, it must accept the parent’s property values.

如果子bean定义中没有指定要使用的bean类,则使用父定义中的bean类,但也可以覆盖它。在后一种情况下(没有指定要用的bean类),子bean定义必须与父bean协作,也就是说,它必须接收父定义的属性值。

A child bean definition inherits scope, constructor argument values, property values, and method overrides from the parent, with the option to add new values. Any scope, initialization method, destroy method, and/or static factory method settings that you specify will override the corresponding parent settings.

子bean定义可以继承作用域,构造函数参数值,属性值,可以重写父方法,可以选择添加新值。你指定的任何作用域,初始化方法,析构方法,和/或静态工厂方法设置将会覆盖对应的父设置。

The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, lazy init.

其余的设置都是从子定义中获取:依赖关系,自动装配模式,依赖检查,单例,延迟初始化。

The preceding example explicitly marks the parent bean definition as abstract by using the abstract attribute. If the parent definition does not specify a class, explicitly marking the parent bean definition as abstract is required, as follows:

前面的例子使用抽象特性将父bean定义显式的标记为abstract。如果父定义没有指定一个类,需要显式的将父bean定义为abstract,形式如下:

1
2
3
4
5
6
7
8
9
10
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>

The parent bean cannot be instantiated on its own because it is incomplete, and it is also explicitly marked as abstract. When a definition is abstract like this, it is usable only as a pure template bean definition that serves as a parent definition for child definitions. Trying to use such an abstract parent bean on its own, by referring to it as a ref property of another bean or doing an explicit getBean() call with the parent bean id, returns an error. Similarly, the container’s internal preInstantiateSingletons() method ignores bean definitions that are defined as abstract.

父bean不能实例化,因为它不完整,并且它被显式的标记为abstract。当一个bean定义是abstract时,它只能是一个纯粹的bean定义模板,作为一个为子定义服务的父定义。当试图使用一个abstract父bean时,可以通过另一个bean的ref属性来引用它或通过父bean的id为参数显式的调用getBean()方法,会返回一个错误。类似的,容器内部的preInstantiateSingletons()方法会忽略抽象bean定义。

ApplicationContext pre-instantiates all singletons by default. Therefore, it is important (at least for singleton beans) that if you have a (parent) bean definition which you intend to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to true, otherwise the application context will actually (attempt to) pre-instantiate the abstract bean.

 

默认情况下ApplicationContext会预实例化所有的单例。因此,如果你想有一个(父)bean定义只作为模板来使用,这个定义中指定了一个类,那你必须确保设置abstract特性为true,否则应用上下文会(试图)预实例化这个abstract bean。

3.8 Container Extension Points

Typically, an application developer does not need to subclass ApplicationContext implementation classes. Instead, the Spring IoC container can be extended by plugging in implementations of special integration interfaces. The next few sections describe these integration interfaces.

通常情况下,应用开发者不需要继承ApplicationContext的实现类。反而是Spring的IoC容器可以通过插入特定集成接口的实现来进行扩展。下面几节将描述这些集成接口。

3.8.1 Customizing beans using a BeanPostProcessor

The BeanPostProcessor interface defines callback methods that you can implement to provide your own (or override the container’s default) instantiation logic, dependency-resolution logic, and so forth. If you want to implement some custom logic after the Spring container finishes instantiating, configuring, and initializing a bean, you can plug in one or more BeanPostProcessor implementations.

BeanPostProcessor接口定义了回调方法,你可以实现这个方法来提供你自己的(或覆盖容器默认的)实例化逻辑,依赖解析逻辑等等。如果你想在Spring容器完成实例化,配置和初始化bean之后实现一些定制的业务逻辑,你可以插入一个或多个BeanPostProcessor实现。

You can configure multiple BeanPostProcessor instances, and you can control the order in which these BeanPostProcessors execute by setting the order property. You can set this property only if the BeanPostProcessor implements the Ordered interface; if you write your own BeanPostProcessor you should consider implementing the Ordered interface too. For further details, consult the javadocs of the BeanPostProcessor and Ordered interfaces. See also the note below on programmatic registration of BeanPostProcessors.

你可以配置多个BeanPostProcessor实例,通过设置order属性你可以控制BeanPostProcessors的执行顺序。只有BeanPostProcessor实现了Ordered接口时你才可以设置这个属性;如果你编写了你自己的BeanPostProcessor,你也应该考虑实现Ordered接口。更多细节请参考BeanPostProcessor接口和Ordered接口的Java文档。也可以查看下面的BeanPostProcessors编程注册的笔记。

BeanPostProcessors operate on bean (or object) instances; that is to say, the Spring IoC container instantiates a bean instance and then BeanPostProcessors do their work.

BeanPostProcessors are scoped per-container. This is only relevant if you are using container hierarchies. If you define a BeanPostProcessor in one container, it will only post-process the beans in that container. In other words, beans that are defined in one container are not post-processed by a BeanPostProcessor defined in another container, even if both containers are part of the same hierarchy.

To change the actual bean definition (i.e., the blueprint that defines the bean), you instead need to use a BeanFactoryPostProcessor as described in Section 3.8.2, “Customizing configuration metadata with a BeanFactoryPostProcessor”.

 

BeanPostProcessors操作一个bean(或对象)实例;也就是说,Spring Ioc容器实例化一个bean实例,然后BeanPostProcessors完成它们的工作。

BeanPostProcessors的作用域是每个容器。只有你在使用容器分层的情况下,这才是相关的。如果你在一个容器中定义了一个BeanPostProcessor,它将只后处理容器中的beans。换句话说,某个容器中定义的beans不能被另一个容器中定义的BeanPostProcessor进行后处理,即使这两个容器是同一层上的一部分。

为了改变实际的bean定义(例如,定义bean的蓝图),你可以使用3.8.2小节中描述的BeanFactoryPostProcessor

The org.springframework.beans.factory.config.BeanPostProcessor interface consists of exactly two callback methods. When such a class is registered as a post-processor with the container, for each bean instance that is created by the container, the post-processor gets a callback from the container both before container initialization methods (such as InitializingBean’s afterPropertiesSet() and any declared init method) are called as well as after any bean initialization callbacks. The post-processor can take any action with the bean instance, including ignoring the callback completely. A bean post-processor typically checks for callback interfaces or may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic.

org.springframework.beans.factory.config.BeanPostProcessor接口包含恰好两个回调方法。当这样一个类在容器中注册为后处理器时,对于容器中创建的每一个bean实例,在容器初始化方法(例如InitializingBeanafterPropertiesSet()方法和任何已声明的初始化方法)被调用之前和任何bean初始化回调函数之后,后处理器会从容器中得到一个回调函数。后处理器可以对bean实例进行任何操作,包括完全忽略回调方法。bean后处理器通常检查回调接口或将bean包裹到代理中。为了提供代理包裹逻辑,一些Spring AOP基础结构类被实现为bean后处理器。

An ApplicationContext automatically detects any beans that are defined in the configuration metadata which implement the BeanPostProcessor interface. The ApplicationContext registers these beans as post-processors so that they can be called later upon bean creation. Bean post-processors can be deployed in the container just like any other beans.

ApplicationContext会自动检测任何配置元数据中定义的实现了BeanPostProcessor接口的bean。为了能在后面bean创建时调用这些bean,ApplicationContext会将这些bean注册为后处理器。bean后处理器可以像其它bean一样在容器进行部署。

Note that when declaring a BeanPostProcessor using an @Bean factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext won’t be able to autodetect it by type before fully creating it. Since a BeanPostProcessor needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.

注意当在一个配置类上使用@Bean声明一个BeanPostProcessor时,工厂方法的返回值应该是实现类本身或是org.springframework.beans.factory.config.BeanPostProcessor接口,这能清晰的表明bean的后处理器特性。此外,在完整的创建它之前,ApplicationContext不能通过类型自动检测它。由于BeanPostProcessor需要早一点实例化,为了在上下文中初始化其它的beans,早期的类型检测是非常关键的。

While the recommended approach for BeanPostProcessor registration is through ApplicationContext auto-detection (as described above), it is also possible to register them programmatically against a ConfigurableBeanFactory using the addBeanPostProcessor method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however that BeanPostProcessors added programmatically do not respect the Ordered interface. Here it is the order of registration that dictates the order of execution. Note also that BeanPostProcessors registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.

 

虽然推荐的注册BeanPostProcessor的方法是通过ApplicationContext自动检测(像前面描述的那样),但也可以通过以编程方法通过使用ConfigurableBeanFactoryaddBeanPostProcessor方法来注册。在注册之前需要评估条件逻辑时,这是非常有用的,或者通过分层中的上下文来复制bean后处理器。注意以编程方式添加的BeanPostProcessors不需要Ordered接口。这种情况下注册顺序意味着执行顺序。注意以编程方式注册的BeanPostProcessors中是在那些通过自动检测注册的BeanPostProcessors之前进行处理,不管任何显式的顺序指定。

 

Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessors are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessors nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.

For any such bean, you should see an informational log message: “Bean foo is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)”.

Note that if you have beans wired into your BeanPostProcessor using autowiring or @Resource (which may fall back to autowiring), Spring might access unexpected beans when searching for type-matching dependency candidates, and therefore make them ineligible for auto-proxying or other kinds of bean post-processing. For example, if you have a dependency annotated with @Resource where the field/setter name does not directly correspond to the declared name of a bean and no name attribute is used, then Spring will access other beans for matching them by type.

 

实现BeanPostProcessor接口的类是特别的并被容器不同对待。所有的BeanPostProcessors和它们直接引用的beans在启动时进行实例化,它们是ApplicationContext特定启动阶段的一部分。接下来,所有BeanPostProcessors以有序形式进行注册,并适用于容器中所有更进一步的beans。由于AOP自动代理是作为BeanPostProcessor本身实现的,既不是BeanPostProcessors也不是它们直接引用的beans适合进行自动代理,因此没有融入它们的方面。

对于这样的bean,你应该看到一个信息日志消息:”Bean foo没资格被所有的BeanPostProcessor接口进行处理(例如,不适合自动代理)。”。

注意如果有beans使用自动装配或@Resource(可能回到自动装配)注入你的BeanPostProcessor,当搜索类型匹配的依赖候选者时,Spring可能访问未预料到beans,因此使它们不适合自动代理或其他类型的进行后处理的bean。例如,如果你有一个带有@Resource注解的依赖,field/setter名称不能直接对应bean声明的名字,也没有使用name特性,Spring将通过类型匹配来访问其它的bean。

The following examples show how to write, register, and use BeanPostProcessors in an ApplicationContext.

下面的例子展示了在ApplicationContext中如何编写,注册和使用BeanPostProcessors

Example: Hello World, BeanPostProcessor-style

This first example illustrates basic usage. The example shows a custom BeanPostProcessor implementation that invokes the toString() method of each bean as it is created by the container and prints the resulting string to the system console.

第一个例子阐述了基本用法。这个例子展示了一个定制BeanPostProcessor实现,实现中调用了每一个bean的toString()方法。当容器创建它时,会将结果字符串输出到系统控制台。

Find below the custom BeanPostProcessor implementation class definition:

下面是定制BeanPostProcessor实现的类定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
return bean; // we could potentially return any object reference here...
}

public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang.xsd">

<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>

<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>

</beans>

Notice how the InstantiationTracingBeanPostProcessor is simply defined. It does not even have a name, and because it is a bean it can be dependency-injected just like any other bean. (The preceding configuration also defines a bean that is backed by a Groovy script. The Spring dynamic language support is detailed in the chapter entitled Chapter 31, Dynamic language support.)

注意InstantiationTracingBeanPostProcessor是怎样简单定义的。它甚至没有一个名字,因为它是一个bean,它能像其它bean一样进行依赖注入。(前面的配置也定义了一个bean,它被Groovy脚本支持。Spring动态语言支持在31章『动态语言支持』中进行了详细描述。)

The following simple Java application executes the preceding code and configuration:

下面的简单Java应用执行了前面的代码和配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}

}

The output of the preceding application resembles the following:

前面的应用输出结果如下:

1
2
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961

Example: The RequiredAnnotationBeanPostProcessor

Using callback interfaces or annotations in conjunction with a custom BeanPostProcessor implementation is a common means of extending the Spring IoC container. An example is Spring’s RequiredAnnotationBeanPostProcessor - a BeanPostProcessor implementation that ships with the Spring distribution which ensures that JavaBean properties on beans that are marked with an (arbitrary) annotation are actually (configured to be) dependency-injected with a value.

使用回调函数接口或注解结合定制BeanPostProcessor实现是扩展Spring IoC容器的常见方法。一个例子是Spring的RequiredAnnotationBeanPostProcessor——一个BeanPostProcessor实现附带在Spring发行中,它保证了标记有(任意)注解的beans上的JavaBean属性能真正(配置成)通过值进行依赖注入。

3.8.2 Customizing configuration metadata with a BeanFactoryPostProcessor

The next extension point that we will look at is the org.springframework.beans.factory.config.BeanFactoryPostProcessor. The semantics of this interface are similar to those of the BeanPostProcessor, with one major difference: BeanFactoryPostProcessor operates on the bean configuration metadata; that is, the Spring IoC container allows a BeanFactoryPostProcessor to read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessors.

接下来我们要看到的扩展点是org.springframework.beans.factory.config.BeanFactoryPostProcessor。这个接口的语义与那些BeanPostProcessor类似,但有一个主要的不同:BeanFactoryPostProcessor可以操作配置元数据;也就是说,Spring IoC容器允许在容器实例化除了BeanFactoryPostProcessor之外的任何beans之前,BeanFactoryPostProcessor读取配置元数据并可能修改它们。

You can configure multiple BeanFactoryPostProcessors, and you can control the order in which these BeanFactoryPostProcessors execute by setting the order property. However, you can only set this property if the BeanFactoryPostProcessor implements the Ordered interface. If you write your own BeanFactoryPostProcessor, you should consider implementing the Ordered interface too. Consult the javadocs of the BeanFactoryPostProcessor and Ordered interfaces for more details.

你可以配置多个BeanFactoryPostProcessors,你可以通过设置order属性来控制这些BeanFactoryPostProcessors的执行顺序。但是,只有BeanFactoryPostProcessor实现了Ordered接口时你才可以设置这个属性。如果你编写了你自己的BeanFactoryPostProcessor,你也应该考虑实现Ordered接口。关于BeanFactoryPostProcessorOrdered的更多细节请看文档。

If you want to change the actual bean instances (i.e., the objects that are created from the configuration metadata), then you instead need to use a BeanPostProcessor (described above in Section 3.8.1, “Customizing beans using a BeanPostProcessor”). While it is technically possible to work with bean instances within a BeanFactoryPostProcessor (e.g., using BeanFactory.getBean()), doing so causes premature bean instantiation, violating the standard container lifecycle. This may cause negative side effects such as bypassing bean post processing.

Also, BeanFactoryPostProcessors are scoped per-container. This is only relevant if you are using container hierarchies. If you define a BeanFactoryPostProcessor in one container, it will only be applied to the bean definitions in that container. Bean definitions in one container will not be post-processed by BeanFactoryPostProcessors in another container, even if both containers are part of the same hierarchy.

 

如果你想改变真正的bean实例(例如,从配置元数据中创建的对象),你应该需要使用BeanPostProcessor(3.8.1小节中描述的)。尽管在BeanFactoryPostProcessor中处理bean实例在技术上是可能的(例如使用BeanFactory.getBean()),但这样做会引起过早的bean实例化,违背标准的容器生命周期。这可能会产生负面影响例如绕过bean后处理。

BeanFactoryPostProcessors的作用域也是在每个容器中。这仅对于容器分层而言。如果在一个容器中你定义了一个BeanFactoryPostProcessor,它将适用于那个容器中的bean定义。一个容器中的bean定义不能被另一个容器中的BeanFactoryPostProcessors进行后处理,即使两个容器是在同一个分层中。

A bean factory post-processor is executed automatically when it is declared inside an ApplicationContext, in order to apply changes to the configuration metadata that define the container. Spring includes a number of predefined bean factory post-processors, such as PropertyOverrideConfigurer and PropertyPlaceholderConfigurer. A custom BeanFactoryPostProcessor can also be used, for example, to register custom property editors.

为了修改定义在容器中的配置元数据,当一个bean工厂后处理器在ApplicationContext中声明时,它会自动执行。Spring包含许多预先定义的bean工厂后处理器,例如PropertyOverrideConfigurerPropertyPlaceholderConfigurer。定制的BeanFactoryPostProcessor也可以使用,例如,为了注册定制的属性编辑器。

An ApplicationContext automatically detects any beans that are deployed into it that implement the BeanFactoryPostProcessor interface. It uses these beans as bean factory post-processors, at the appropriate time. You can deploy these post-processor beans as you would any other bean.

ApplicationContext会自动检测任何部署在它之内的实现了BeanFactoryPostProcessor接口的bean。在合适的时间,它会使用这些beans作为bean工厂后处理器。你可以像任何你使用的bean那样部署这些后处理器beans。

As with BeanPostProcessors, you typically do not want to configure BeanFactoryPostProcessors for lazy initialization. If no other bean references a Bean(Factory)PostProcessor, that post-processor will not get instantiated at all. Thus, marking it for lazy initialization will be ignored, and the Bean(Factory)PostProcessor will be instantiated eagerly even if you set the default-lazy-init attribute to trueon the declaration of your <beans/> element.

 

关于BeanPostProcessors, 通常情况下你不想配置BeanFactoryPostProcessors为延迟初始化。 如果没有别的bean引用Bean(Factory)PostProcessor,后处理器将不会实例化。因此,对它进行延迟初始化会被忽略,即使你将<beans/>元素中的default-lazy-init特性设置为trueBean(Factory)PostProcessor也会急切的初始化。

Example: the Class name substitution PropertyPlaceholderConfigurer

You use the PropertyPlaceholderConfigurer to externalize property values from a bean definition in a separate file using the standard Java Properties format. Doing so enables the person deploying an application to customize environment-specific properties such as database URLs and passwords, without the complexity or risk of modifying the main XML definition file or files for the container.

你可以使用PropertyPlaceholderConfigurer读取单独文件中的bean定义来使属性具体化,这个单独文件使用标准的Java Properties格式。这样做可以在部署应用时定制特定环境属性例如数据库URLs和密码,没有复杂性或修改主XML定义文件及容器相关文件的风险。

Consider the following XML-based configuration metadata fragment, where a DataSource with placeholder values is defined. The example shows properties configured from an external Properties file. At runtime, a PropertyPlaceholderConfigurer is applied to the metadata that will replace some properties of the DataSource. The values to replace are specified as placeholders of the form ${property-name} which follows the Ant/log4j/JSP EL style.

考虑一下下面的基于XML定义的配置元数据片段,其中定义了一个带有占位符的DataSource。这个例子展示了从外部Properties文件进行属性配置。在运行时,PropertyPlaceholderConfigurer会应用到元数据中,将会替换DataSource中的一些属性。通过${property-name}形式的占位符指定要替换的值,这遵循了Ant/log4j/JSP EL风格。

1
2
3
4
5
6
7
8
9
10
11
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

The actual values come from another file in the standard Java Properties format:

真正的属性值来自于另一个以标准Java Properties形式编写的文件:

1
2
3
4
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

Therefore, the string ${jdbc.username} is replaced at runtime with the value ‘sa’, and the same applies for other placeholder values that match keys in the properties file. The PropertyPlaceholderConfigurer checks for placeholders in most properties and attributes of a bean definition. Furthermore, the placeholder prefix and suffix can be customized.

因此,在运行是字符串${jdbc.username}被替换为sa,其它的匹配属性文件中的key的占位符的值以同样方式替换。PropertyPlaceholderConfigurer会检查bean中大多数属性和特性的占位符。此外,占位符的前缀和后缀都可以定制。

With the context namespace introduced in Spring 2.5, it is possible to configure property placeholders with a dedicated configuration element. One or more locations can be provided as a comma-separated list in the location attribute.

Spring 2.5中引入了上下文命名空间,可以通过专用配置元素配置属性占位符。在location特性可以提供一个或多个位置,多个位置用逗号分开。

1
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>

The PropertyPlaceholderConfigurer not only looks for properties in the Properties file you specify. By default it also checks against the Java System properties if it cannot find a property in the specified properties files. You can customize this behavior by setting the systemPropertiesMode property of the configurer with one of the following three supported integer values:

  • never (0): Never check system properties

  • fallback (1): Check system properties if not resolvable in the specified properties files. This is the default.

  • override (2): Check system properties first, before trying the specified properties files. This allows system properties to override any other property source.

PropertyPlaceholderConfigurer不仅仅查找指定Properties文件中的属性。默认情况下,如果不能在指定属性文件中找到属性,它也检查Java System属性。你可以通过下面三个支持的整数值中的一个设置配置器的systemPropertiesMode属性,从而定制查找行为。

  • never (0): 从不检查system属性

  • fallback (1): 如果不能在指定文件中解析属性,检查system属性,这是默认值。

  • override (2): 在查找指定文件之前,首先检查system属性,这可以使系统属性覆盖任何其它属性源。

Consult the PropertyPlaceholderConfigurer javadocs for more information.

更多信息请看PropertyPlaceholderConfigurer文档。

You can use the PropertyPlaceholderConfigurer to substitute class names, which is sometimes useful when you have to pick a particular implementation class at runtime. For example:

你可以PropertyPlaceholderConfigurer替换类名,有时候非常有用,特别是运行时你必须选择一个特别的实现类的情况下。例如:

1
2
3
4
5
6
7
8
9
10
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:com/foo/strategy.properties</value>
</property>
<property name="properties">
<value>custom.strategy.class=com.foo.DefaultStrategy</value>
</property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>

If the class cannot be resolved at runtime to a valid class, resolution of the bean fails when it is about to be created, which is during the preInstantiateSingletons() phase of an ApplicationContext for a non-lazy-init bean.

如果这个类不能在运行时解析成一个有效类,对于一个非懒惰初始化的bean,当它要创建时,在ApplicationContextpreInstantiateSingletons()期间,bean会解析失败。

Example: the PropertyOverrideConfigurer

The PropertyOverrideConfigurer, another bean factory post-processor, resembles the PropertyPlaceholderConfigurer, but unlike the latter, the original definitions can have default values or no values at all for bean properties. If an overriding Properties file does not have an entry for a certain bean property, the default context definition is used.

PropertyOverrideConfigurer,另一个bean工厂后处理器,类似于PropertyPlaceholderConfigurer,但不像后者,最初的定义可以有默认值或bean属性一点也没有值。如果一个覆写的Properties文件对于某个bean属性没有任何输入,会使用默认的上下文定义。

Note that the bean definition is not aware of being overridden, so it is not immediately obvious from the XML definition file that the override configurer is being used. In case of multiple PropertyOverrideConfigurer instances that define different values for the same bean property, the last one wins, due to the overriding mechanism.

注意bean定义没有意识到被覆写了,因此从XML定义文件中它不能立刻很明显的看出在使用覆写的配置器。为了防止多个PropertyOverrideConfigurer实例对于同一个bean属性定义不同的值,根据覆写机制,使用最后一个定义的值。

Properties file configuration lines take this format:

属性文件配置形式如下:

1
beanName.property=value

For example:

例如:

1
2
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

This example file can be used with a container definition that contains a bean called dataSource, which has driver and url properties.

例子文件可以被包含名为dataSource bean的容器定义使用,它有一个driverurl属性。

Compound property names are also supported, as long as every component of the path except the final property being overridden is already non-null (presumably initialized by the constructors). In this example…​

混合属性命名也支持,除了最后被覆写的属性,只要路径的每部分都已经是非空(假设构造函数进行初始化)。在这个例子中:

1
foo.fred.bob.sammy=123

the sammy property of the bob property of the fred property of the foo bean is set to the scalar value 123.

foo bean中的fred属性的bob属性的sammy属性设为标量值123

Specified override values are always literal values; they are not translated into bean references. This convention also applies when the original value in the XML bean definition specifies a bean reference.

 

指定的覆写值总是字面值;它们不能转成bean引用。当XML bean定义中的初始值指定了一个bean引用时,这个规范同样有效。

With the context namespace introduced in Spring 2.5, it is possible to configure property overriding with a dedicated configuration element:

Spring 2.5引入了上下文命名空间,可以用专用配置元素配置属性覆写:

1
<context:property-override location="classpath:override.properties"/>

3.8.3 Customizing instantiation logic with a FactoryBean

Implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories.

为对象实现org.springframework.beans.factory.FactoryBean接口的是工厂本身。

The FactoryBean interface is a point of pluggability into the Spring IoC container’s instantiation logic. If you have complex initialization code that is better expressed in Java as opposed to a (potentially) verbose amount of XML, you can create your own FactoryBean, write the complex initialization inside that class, and then plug your custom FactoryBean into the container.

FactoryBean接口是Spring IoC的实例化逻辑可插入性的一个点。如果你有复杂的初始化代码,相比于大量的冗余的XML代码用Java语言来表达会更好,那么你可以创建你自己的FactoryBean,在类里面编写复杂的初始化逻辑,并将你定制的FactoryBean插入到容器中。

The FactoryBean interface provides three methods:

  • Object getObject(): returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.

  • boolean isSingleton(): returns true if this FactoryBean returns singletons, false otherwise.

  • Class getObjectType(): returns the object type returned by the getObject() method or null if the type is not known in advance.

FactoryBean接口提供了三个方法:

  • Object getObject(): 返回一个工厂创建的对象实例。这个实例可能被共享, 依赖于工厂是否返回一个单例或原型。

  • boolean isSingleton(): 如果FactoryBean返回单例,返回true,否则返回false。

  • Class getObjectType(): 返回getObject()方法返回的类型,如果类型不能提前知道则返回null。

The FactoryBean concept and interface is used in a number of places within the Spring Framework; more than 50 implementations of the FactoryBean interface ship with Spring itself.

FactoryBean的概念和接口在Spring框架中的许多地方都使用了;Spring本身中有不止50个FactoryBean接口的实现。

When you need to ask a container for an actual FactoryBean instance itself instead of the bean it produces, preface the bean’s id with the ampersand symbol (&) when calling the getBean() method of the ApplicationContext. So for a given FactoryBean with an id of myBean, invoking getBean("myBean") on the container returns the product of the FactoryBean; whereas, invoking getBean("&myBean") returns the FactoryBean instance itself.

当你需要向容器请求一个真正的FactoryBean实例本身来代替它产生的bean时,调用ApplicationContextgetBean()方法时,bean的id前面要加上一个$符。因此给定一个id为myBeanFactoryBean,在容器中调用getBean("myBean"),返回FactoryBean的产品,但调用getBean("&myBean")会返回FactoryBean实例本身。

3.9 Annotation-based container configuration

Are annotations better than XML for configuring Spring?

The introduction of annotation-based configurations raised the question of whether this approach is ‘better’ than XML. The short answer is it depends. The long answer is that each approach has its pros and cons, and usually it is up to the developer to decide which strategy suits them better. Due to the way they are defined, annotations provide a lot of context in their declaration, leading to shorter and more concise configuration. However, XML excels at wiring up components without touching their source code or recompiling them. Some developers prefer having the wiring close to the source while others argue that annotated classes are no longer POJOs and, furthermore, that the configuration becomes decentralized and harder to control.

No matter the choice, Spring can accommodate both styles and even mix them together. It’s worth pointing out that through its JavaConfig option, Spring allows annotations to be used in a non-invasive way, without touching the target components source code and that in terms of tooling, all configuration styles are supported by the Spring Tool Suite.

 

在配置Spring时注解是否比XML更好?

基于注解配置的引入引出了一个问题——这种方式是否比基于XML的配置更好。简短的回答是视情况而定。长一点的回答是每种方法都有它的优点和缺点,通常是由开发者决定哪一种策略更适合他们。由于注解的定义方式,注解在它们的声明中提供了许多上下文,导致配置更简短更简洁。然而,XML擅长连接组件而不必接触源代码或重新编译它们。一些开发者更喜欢接近源代码,而另一些人则认为基于注解的类不再是POJOs,此外,配置变的去中心化,而且更难控制。

无论选择是什么,Spring都能容纳这两种风格,甚至可以将它们混合在一起。值得指出的是,通过它的Java配置选项,Spring允许注解以一种非入侵的方式使用,不触碰目标组件源码和那些工具,所有的配置风格由Spring工具套件支持。

An alternative to XML setups is provided by annotation-based configuration which rely on the bytecode metadata for wiring up components instead of angle-bracket declarations. Instead of using XML to describe a bean wiring, the developer moves the configuration into the component class itself by using annotations on the relevant class, method, or field declaration. As mentioned in the section called “Example: The RequiredAnnotationBeanPostProcessor”, using a BeanPostProcessor in conjunction with annotations is a common means of extending the Spring IoC container. For example, Spring 2.0 introduced the possibility of enforcing required properties with the @Required annotation. Spring 2.5 made it possible to follow that same general approach to drive Spring’s dependency injection. Essentially, the @Autowired annotation provides the same capabilities as described in Section 3.4.5, “Autowiring collaborators” but with more fine-grained control and wider applicability. Spring 2.5 also added support for JSR-250 annotations such as @PostConstruct, and @PreDestroy. Spring 3.0 added support for JSR-330 (Dependency Injection for Java) annotations contained in the javax.inject package such as @Inject and @Named. Details about those annotations can be found in the relevant section.

基于注解的配置提供了一种XML设置的可替代方式,它依赖于字节码元数据来组装组件,而不是用尖括号声明的方式。代替使用XML来描述bean组装,开发者通过将注解使用在相关的类,方法或字段声明中,将配置移动到了组件类本身的内部。正如在“Example: The RequiredAnnotationBeanPostProcessor”那节提到的那样,使用BeanPostProcessor与注解结合是扩展Spring IoC容器的的常见方法。例如,Spring 2.0引入了@Required注解来执行需要的属性的可能性。Spring 2.5使以同样地通用方法来驱动Spring的依赖注入变为可能。本质上来说,@Autowired提供了如3.4.5小节描述的同样的能力。“Autowiring collaborators”但更细粒度的控制和更广的应用性。Spring 2.5也添加对JSR-250注解的支持,例如,@PostConstruct@PreDestroy
。Spring 3.0添加了对JSR-330,包含在javax.inject包内的注解(Java的依赖注入)的支持,例如@Inject@Named。关于这些注解的细节可以在相关的小节找到。

Annotation injection is performed before XML injection, thus the latter configuration will override the former for properties wired through both approaches.

 

注解注入在XML注入之前进行,因此对于通过两种方法进行组装的属性后者的配置会覆盖前者。

As always, you can register them as individual bean definitions, but they can also be implicitly registered by including the following tag in an XML-based Spring configuration (notice the inclusion of the context namespace):

跟以前一样,你可以作为单独的bean定义来注册它们,但也可以通过在一个基于XML的Spring配置(注入包含上下文命名空间)中包含下面的标签来隐式的注册它们:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

</beans>

(The implicitly registered post-processors include AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, as well as the aforementioned RequiredAnnotationBeanPostProcessor.)

(隐式注册的后处理器包括 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor和前面提到的RequiredAnnotationBeanPostProcessor。)

<context:annotation-config/> only looks for annotations on beans in the same application context in which it is defined. This means that, if you put <context:annotation-config/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Autowired beans in your controllers, and not your services. See Section 18.2, “The DispatcherServlet” for more information.

 

<context:annotation-config/>仅在定义它的同样的应用上下文中寻找注解的beans。这意味着,如果你在一个为DispatcherServlet服务的WebApplicationContext中放置了<context:annotation-config/>,它只能在你的控制器中寻找@Autowired注解的beans,而不是在你的服务层中。更多信息请看18.2小节,“The DispatcherServlet”。

3.9.1 @Required

The @Required annotation applies to bean property setter methods, as in the following example:

@Required注解应用到bean属性的setter方法上,例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleMovieLister {

private MovieFinder movieFinder;

@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...

}

This annotation simply indicates that the affected bean property must be populated at configuration time, through an explicit property value in a bean definition or through autowiring. The container throws an exception if the affected bean property has not been populated; this allows for eager and explicit failure, avoiding NullPointerExceptions or the like later on. It is still recommended that you put assertions into the bean class itself, for example, into an init method. Doing so enforces those required references and values even when you use the class outside of a container.

这个注解仅仅是表明受影响的bean属性必须在配置时通过显式的bean定义或自动组装填充。如果受影响的bean属性没有填充,容器会抛出一个异常,这允许及早明确的失败,避免NullPointerExceptions或后面出现类似的情况。仍然建议你在bean类本身加入断言,例如,加入到初始化方法中。这样做可以强制这些需要的引用和值,甚至是你在容器外部使用这个类的时候。

3.9.2 @Autowired

JSR 330’s @Inject annotation can be used in place of Spring’s @Autowired annotation in the examples below. See here for more details.

 

在下面的例子中JSR 330的@Inject注解可以用来代替Spring的@Autowired注解。

You can apply the @Autowired annotation to constructors:

你可以将@Autowired注解应用到构造函数上。

1
2
3
4
5
6
7
8
9
10
11
12
public class MovieRecommender {

private final CustomerPreferenceDao customerPreferenceDao;

@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}

// ...

}

As of Spring Framework 4.3, the @Autowired constructor is no longer necessary if the target bean only defines one constructor. If several constructors are available, at least one must be annotated to teach the container which one it has to use.

 

从Spring框架4.3起,如果目标bena仅定义了一个构造函数,那么@Autowired注解的构造函数不再是必要的。如果一些构造函数是可获得的,至少有一个必须要加上注解,以便于告诉容器使用哪一个。

As expected, you can also apply the @Autowired annotation to “traditional” setter methods:

正如预料的那样,你也可以将@Autowired注解应用到“传统的”setter方法上:

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...

}

You can also apply the annotation to methods with arbitrary names and/or multiple arguments:

你也可以应用注解到具有任何名字和/或多个参数的方法上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MovieRecommender {

private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}

// ...

}

You can apply @Autowired to fields as well and even mix it with constructors:

你也可以应用@Autowired到字段上,甚至可以与构造函数混合用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MovieRecommender {

private final CustomerPreferenceDao customerPreferenceDao;

@Autowired
private MovieCatalog movieCatalog;

@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}

// ...

}

It is also possible to provide all beans of a particular type from the ApplicationContext by adding the annotation to a field or method that expects an array of that type:

通过给带有数组的字段或方法添加@Autowired注解,也可以从ApplicationContext中提供一组特定类型的bean:

1
2
3
4
5
6
7
8
public class MovieRecommender {

@Autowired
private MovieCatalog[] movieCatalogs;

// ...

}

The same applies for typed collections:

同样也可以应用到具有同一类型的集合上:

1
2
3
4
5
6
7
8
9
10
11
12
public class MovieRecommender {

private Set<MovieCatalog> movieCatalogs;

@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}

// ...

}

Your beans can implement the org.springframework.core.Ordered interface or either use the @Order or standard @Priority annotation if you want items in the array or list to be sorted into a specific order.

 

如果你希望数组或列表中的项按指定顺序排序,你的bean可以实现org.springframework.core.Ordered接口,或使用@Order或标准@Priority注解。

Even typed Maps can be autowired as long as the expected key type is String. The Map values will contain all beans of the expected type, and the keys will contain the corresponding bean names:

只要期望的key是String,那么类型化的Maps就可以自动组装。Map的值将包含所有期望类型的beans,key将包含对应的bean名字:

1
2
3
4
5
6
7
8
9
10
11
12
public class MovieRecommender {

private Map<String, MovieCatalog> movieCatalogs;

@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}

// ...

}

By default, the autowiring fails whenever zero candidate beans are available; the default behavior is to treat annotated methods, constructors, and fields as indicating required dependencies. This behavior can be changed as demonstrated below.

默认情况下,当没有候选beans可获得时,自动组装会失败;默认的行为是将注解的方法,构造函数和字段看作指明了需要的依赖。这个行为也可以通过下面的方式去改变。

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...

}

Only one annotated constructor per-class can be marked as required, but multiple non-required constructors can be annotated. In that case, each is considered among the candidates and Spring uses the greediest constructor whose dependencies can be satisfied, that is the constructor that has the largest number of arguments.

@Autowired’s required attribute is recommended over the @Required annotation. The required attribute indicates that the property is not required for autowiring purposes, the property is ignored if it cannot be autowired. @Required, on the other hand, is stronger in that it enforces the property that was set by any means supported by the container. If no value is injected, a corresponding exception is raised.

 

每个类只有一个构造函数可以标记为必需的,但可以注解多个非必需的构造函数。在这种情况下,会考虑这些候选者中的每一个,Spring使用最贪婪的构造函数,即依赖最满足的构造函数,具有最大数目的参数。

建议在@Required注解之上使用@Autowiredrequired特性。required特性表明这个属性自动装配是不需要的,如果这个属性不能被自动装配,它会被忽略。另一方面@Required是更强大的,在它强制这个属性被任何容器支持的bean设置。如果没有值注入,会抛出对应的异常。

You can also use @Autowired for interfaces that are well-known resolvable dependencies: BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, and MessageSource. These interfaces and their extended interfaces, such as ConfigurableApplicationContext or ResourcePatternResolver, are automatically resolved, with no special setup necessary.

你也可以对那些已知的具有可解析依赖的接口使用@AutowiredBeanFactoryApplicationContextEnvironment, ResourceLoaderApplicationEventPublisherMessageSource。这些接口和它们的扩展接口,例如ConfigurableApplicationContextResourcePatternResolver,可以自动解析,不需要特别的设置。

1
2
3
4
5
6
7
8
9
10
11
public class MovieRecommender {

@Autowired
private ApplicationContext context;

public MovieRecommender() {
}

// ...

}

@Autowired, @Inject, @Resource, and @Value annotations are handled by Spring BeanPostProcessor implementations which in turn means that you cannot apply these annotations within your own BeanPostProcessor or BeanFactoryPostProcessor types (if any). These types must be ‘wired up’ explicitly via XML or using a Spring @Bean method.

 

@Autowired@Inject@Resource@Value注解是通过Spring BeanPostProcessor实现处理,这反过来意味着你不能在你自己的BeanPostProcessorBeanFactoryPostProcessor中应用这些注解(如果有的话)。这些类型必须显式的通过XML或使用Spring的@Bean方法来’wired up’。

3.9.3 Fine-tuning annotation-based autowiring with @Primary

Because autowiring by type may lead to multiple candidates, it is often necessary to have more control over the selection process. One way to accomplish this is with Spring’s @Primary annotation. @Primary indicates that a particular bean should be given preference when multiple beans are candidates to be autowired to a single-valued dependency. If exactly one ‘primary’ bean exists among the candidates, it will be the autowired value.

因为根据类型的自动装配可能会导致多个候选目标,所以在选择过程中进行更多的控制经常是有必要的。一种方式通过Spring的@Primary注解来完成。当有个多个候选bean要组装到一个单值的依赖时,@Primary表明指定的bean应该具有更高的优先级。如果确定一个’primary’ bean位于候选目标中间,它将是那个自动装配的值。

Let’s assume we have the following configuration that defines firstMovieCatalog as the primary MovieCatalog.

假设我们具有如下配置,将firstMovieCatalog定义为主要的MovieCatalog

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class MovieConfiguration {

@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }

@Bean
public MovieCatalog secondMovieCatalog() { ... }

// ...

}

With such configuration, the following MovieRecommender will be autowired with the firstMovieCatalog.

根据这样的配置,下面的MovieRecommender将用firstMovieCatalog进行自动装配。

1
2
3
4
5
6
7
8
public class MovieRecommender {

@Autowired
private MovieCatalog movieCatalog;

// ...

}

The corresponding bean definitions appear as follows.

对应的bean定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>

<bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

3.9.4 Fine-tuning annotation-based autowiring with qualifiers

@Primary is an effective way to use autowiring by type with several instances when one primary candidate can be determined. When more control over the selection process is required, Spring’s @Qualifier annotation can be used. You can associate qualifier values with specific arguments, narrowing the set of type matches so that a specific bean is chosen for each argument. In the simplest case, this can be a plain descriptive value:

当有多个实例需要确定一个主要的候选对象时,@Primary是一种按类型自动装配的有效方式。当需要在选择过程中进行更多的控制时,可以使用Spring的@Qualifier注解。为了给每个选择一个特定的bean,你可以将限定符的值与特定的参数联系在一起,减少类型匹配集合。在最简单的情况下,这是一个纯描述性值:

1
2
3
4
5
6
7
8
9
public class MovieRecommender {

@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;

// ...

}

The @Qualifier annotation can also be specified on individual constructor arguments or method parameters:

@Qualifier注解也可以指定单个构造函数参数或方法参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MovieRecommender {

private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}

// ...

}

The corresponding bean definitions appear as follows. The bean with qualifier value “main” is wired with the constructor argument that is qualified with the same value.

对应的bean定义如下。限定符值为”main”的bean被组装到有相同值的构造函数参数中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>

<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>

<!-- inject any dependencies required by this bean -->
</bean>

<bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

For a fallback match, the bean name is considered a default qualifier value. Thus you can define the bean with an id “main” instead of the nested qualifier element, leading to the same matching result. However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches; they do not semantically express a reference to a unique bean id. Good qualifier values are “main” or “EMEA” or “persistent”, expressing characteristics of a specific component that are independent from the bean id, which may be auto-generated in case of an anonymous bean definition like the one in the preceding example.

对于回退匹配,bean名字被认为是默认的限定符值。因此你可以定义一个id为main的bean来代替内嵌的限定符元素,会有同样的匹配结果。然而,尽管你可以使用这个约定根据名字引用特定的beans,但是@Autowired从根本上来讲是使用可选的语义限定符来进行类型驱动注入的。这意味着限定符的值,即使回退到bean名称,总是缩小语义类型匹配的集合;它们没有从语义上将一个引用表达为一个唯一的bean id。好的限定符值是”main”或”EMEA”或”persistent”,表达一个特定组件的性质,这个组件是独立于bean id的,即使前面例子中像这个bean一样的匿名bean会自动生成id。

Qualifiers also apply to typed collections, as discussed above, for example, to Set<MovieCatalog>. In this case, all matching beans according to the declared qualifiers are injected as a collection. This implies that qualifiers do not have to be unique; they rather simply constitute filtering criteria. For example, you can define multiple MovieCatalog beans with the same qualifier value “action”, all of which would be injected into a Set<MovieCatalog> annotated with @Qualifier("action").

正如前面讨论的那样,限定符也可以应用到类型结合上,例如,Set<MovieCatalog>。在这个例子中,根据声明的限定符匹配的所有beans作为一个集合进行注入。这意味着限定符不必是唯一的;它们只是构成过滤标准。例如,你可以定义多个具有同样限定符值”action”的MovieCatalog,所有的这些都将注入到带有注解@Qualifier("action")Set<MovieCatalog>中。

If you intend to express annotation-driven injection by name, do not primarily use @Autowired, even if is technically capable of referring to a bean name through @Qualifier values. Instead, use the JSR-250 @Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process. @Autowired has rather different semantics: After selecting candidate beans by type, the specified String qualifier value will be considered within those type-selected candidates only, e.g. matching an “account” qualifier against beans marked with the same qualifier label.

For beans that are themselves defined as a collection/map or array type, @Resource is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, collection/map and array types can be matched through Spring’s @Autowired type matching algorithm as well, as long as the element type information is preserved in @Bean return type signatures or collection inheritance hierarchies. In this case, qualifier values can be used to select among same-typed collections, as outlined in the previous paragraph.

As of 4.3, @Autowired also considers self references for injection, i.e. references back to the bean that is currently injected. Note that self injection is a fallback; regular dependencies on other components always have precedence. In that sense, self references do not participate in regular candidate selection and are therefore in particular never primary; on the contrary, they always end up as lowest precedence. In practice, use self references as a last resort only, e.g. for calling other methods on the same instance through the bean’s transactional proxy: Consider factoring out the affected methods to a separate delegate bean in such a scenario. Alternatively, use @Resource which may obtain a proxy back to the current bean by its unique name.

@Autowired applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. By contrast, @Resource is supported only for fields and bean property setter methods with a single argument. As a consequence, stick with qualifiers if your injection target is a constructor or a multi-argument method.

@Autowired可以应用到字段,构造函数和多参数方法上,允许通过限定符注解在参数层面上缩减候选目标。相比之下,@Resource仅支持字段和bean属性的带有单个参数的setter方法。因此,如果你的注入目标是一个构造函数或一个多参数的方法,坚持使用限定符。

 

如果你想通过名字表达注解驱动的注入,不要主要使用@Autowired,虽然在技术上能通过@Qualifier值引用一个bean名字。作为可替代产品,可以使用JSR-250 @Resource注解,它在语义上被定义为通过组件唯一的名字来识别特定的目标组件,声明的类型与匹配过程无关。@Autowired有不同的语义:通过类型选择候选beans,特定的String限定符值被认为只在类型选择的候选目标中,例如,在那些标记为具有相同限定符标签的beans中匹配一个”account”限定符。

对于那些本身定义在集合/映射或数组类型中的beans来说,@Resource是一个很好的解决方案,适用于特定的集合或通过唯一名字区分的数组bean。也就是说,自Spring 4.3起,集合/映射和数组类型中也可以通过Spring的@Autowired类型匹配算法进行匹配,只要元素类型信息在@Bean中保留,返回类型签名或集合继承体系。在这种情况下,限定符值可以用来在相同类型的集合中选择,正如在前一段中概括的那样。

自Spring 4.3起,@Autowired也考虑自引用注入,例如,引用返回当前注入的bean。注意自注入是备用;普通对其它组件的依赖关系总是优先的。在这个意义上,自引用不参与普通的候选目标选择,因此尤其是从不是主要的;恰恰相反,它们最终总是最低的优先级。在实践中,自引用只是作为最后的手段,例如,通过bean的事务代理调用同一实例的其它方法:在考虑抽出受影响的方法来分隔代理bean的场景中。或者,使用@Resource通过它的唯一名字可能得到一个返回当前bean的代理。

@Autowired可以应用到字段,构造函数和多参数方法上,允许通过限定符注解在参数层面上缩减候选目标。相比之下,@Resource仅支持字段和bean属性的带有单个参数的setter方法。因此,如果你的注入目标是一个构造函数或一个多参数的方法,坚持使用限定符。

You can create your own custom qualifier annotations. Simply define an annotation and provide the @Qualifier annotation within your definition:

你可以创建自己的定制限定符注解。简单定义一个注解,在你自己的定义中提供@Qualifier注解:

1
2
3
4
5
6
7
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

String value();
}

Then you can provide the custom qualifier on autowired fields and parameters:

然后你可以在自动装配的字段和参数上提供定制的限定符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MovieRecommender {

@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;

@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}

// ...

}

Next, provide the information for the candidate bean definitions. You can add <qualifier/> tags as sub-elements of the <bean/> tag and then specify the type and value to match your custom qualifier annotations. The type is matched against the fully-qualified class name of the annotation. Or, as a convenience if no risk of conflicting names exists, you can use the short class name. Both approaches are demonstrated in the following example.

接下来,提供候选bean定义的信息。你可以添加<qualifier/>标记作为<bean/>标记的子元素,然后指定匹配你的定制限定符注解的类型和值。类型用来匹配注解的全限定类名称。或者,如果没有名称冲突的风险,为了方便,你可以使用简写的类名称。下面的例子证实了这些方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<qualifier type="example.Genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>

<bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

In Section 3.10, “Classpath scanning and managed components”, you will see an annotation-based alternative to providing the qualifier metadata in XML. Specifically, see Section 3.10.8, “Providing qualifier metadata with annotations”.

在3.10小节,“类路径扫描和管理组件”中,你将看到一个基于注解的替代方法,在XML中提供限定符元数据。特别地,看3.10.8小节,“用注解提供限定符元数据”。

In some cases, it may be sufficient to use an annotation without a value. This may be useful when the annotation serves a more generic purpose and can be applied across several different types of dependencies. For example, you may provide an offline catalog that would be searched when no Internet connection is available. First define the simple annotation:

在某些情况下,使用没有值的注解就是足够的。当注解为了通用的目的时,这是非常有用的,可以应用到跨几个不同类型的依赖上。例如,当网络不可用时,你可以提供一个要搜索的离线目录。首先定义一个简单的注解:

1
2
3
4
5
6
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

Then add the annotation to the field or property to be autowired:

然后将注解添加到要自动装配的字段或属性上:

1
2
3
4
5
6
7
8
9
public class MovieRecommender {

@Autowired
@Offline
private MovieCatalog offlineCatalog;

// ...

}

Now the bean definition only needs a qualifier type:

现在bean定义只需要一个限定符类型:

1
2
3
4
<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/>
<!-- inject any dependencies required by this bean -->
</bean>

You can also define custom qualifier annotations that accept named attributes in addition to or instead of the simple value attribute. If multiple attribute values are then specified on a field or parameter to be autowired, a bean definition must match all such attribute values to be considered an autowire candidate. As an example, consider the following annotation definition:

你也可以定义接收命名属性之外的定制限定符注解或代替简单的值属性。如果要注入的字段或参数指定了多个属性值,bean定义必须匹配所有的属性值才会被认为是一个可自动装配的候选目标。作为一个例子,考虑下面的注解定义:

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

String genre();

Format format();

}

In this case Format is an enum:

这种情况下Format是枚举类型:

1
2
3
public enum Format {
VHS, DVD, BLURAY
}

The fields to be autowired are annotated with the custom qualifier and include values for both attributes: genre and format.

要自动装配的字段使用定制限定符进行注解,并且包含了两个属性值:genreformat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MovieRecommender {

@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;

@Autowired
@MovieQualifier(format=Format.VHS, genre="Comedy")
private MovieCatalog comedyVhsCatalog;

@Autowired
@MovieQualifier(format=Format.DVD, genre="Action")
private MovieCatalog actionDvdCatalog;

@Autowired
@MovieQualifier(format=Format.BLURAY, genre="Comedy")
private MovieCatalog comedyBluRayCatalog;

// ...

}

Finally, the bean definitions should contain matching qualifier values. This example also demonstrates that bean meta attributes may be used instead of the <qualifier/> sub-elements. If available, the <qualifier/> and its attributes take precedence, but the autowiring mechanism falls back on the values provided within the <meta/> tags if no such qualifier is present, as in the last two bean definitions in the following example.

最后,bean定义应该包含匹配的限定符值。这个例子也证实了bean元属性可以用来代替<qualifier/>子元素。如果可获得<qualifier/>,它和它的属性优先级更高,如果当前没有限定符,自动装配机制会将<meta/>内的值作为备用,正如下面的例子中的最后两个bean定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Comedy"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<meta key="format" value="DVD"/>
<meta key="genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<meta key="format" value="BLURAY"/>
<meta key="genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>

</beans>

3.9.5 Using generics as autowiring qualifiers

In addition to the @Qualifier annotation, it is also possible to use Java generic types as an implicit form of qualification. For example, suppose you have the following configuration:

除了@Qualifier注解外,也可以使用Java的泛型类型作为限定符的一种隐式方式。例如,假设你有如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class MyConfiguration {

@Bean
public StringStore stringStore() {
return new StringStore();
}

@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}

}

Assuming that beans above implement a generic interface, i.e. Store<String> and Store<Integer>, you can @Autowire the Store interface and the generic will be used as a qualifier:

假设上面的beans实现了一个泛型接口,例如,Store<String>Store<Integer>,你可以@Autowire Store接口,泛型将作为限定符使用:

1
2
3
4
5
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

Generic qualifiers also apply when autowiring Lists, Maps and Arrays:

当自动装配ListsMapsArrays时,也会应用泛型限定符:

1
2
3
4
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

3.9.6 CustomAutowireConfigurer

The CustomAutowireConfigurer is a BeanFactoryPostProcessor that enables you to register your own custom qualifier annotation types even if they are not annotated with Spring’s @Qualifier annotation.

CustomAutowireConfigurer是一个能使你注册自己的定制限定符注解类型的BeanFactoryPostProcessor,即使它们不能使用Spring的@Qualifier注解进行注解。

1
2
3
4
5
6
7
8
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>

The AutowireCandidateResolver determines autowire candidates by:

  • the autowire-candidate value of each bean definition

  • any default-autowire-candidates pattern(s) available on the <beans/> element

  • the presence of @Qualifier annotations and any custom annotations registered with the CustomAutowireConfigurer

AutowireCandidateResolver通过下面的方式决定自动装配的候选目标:

  • 每个bean定义的autowire-candidate

  • <beans/>元素可获得的任何default-autowire-candidates模式

  • 存在@Qualifier注解和任何在CustomAutowireConfigurer中注册的定制注解

When multiple beans qualify as autowire candidates, the determination of a “primary” is the following: if exactly one bean definition among the candidates has a primary attribute set to true, it will be selected.

当多个beans符合条件成为自动装配的候选目标时,”primary” bean的决定如下:如果在候选目标中某个确定的bean中的primary特性被设为true,它将被选为目标bean。

3.9.7 @Resource

Spring also supports injection using the JSR-250 @Resource annotation on fields or bean property setter methods. This is a common pattern in Java EE 5 and 6, for example in JSF 1.2 managed beans or JAX-WS 2.0 endpoints. Spring supports this pattern for Spring-managed objects as well.

Spring也支持使用JSR-250 @Resource对字段或bean属性setter方法进行注入。这是在Java EE 5和6中的一种通用模式,例如在JSF 1.2管理的beans或JAX-WS 2.0的端点。Spring对它管理的对象也支持这种模式。

@Resource takes a name attribute, and by default Spring interprets that value as the bean name to be injected. In other words, it follows by-name semantics, as demonstrated in this example:

@Resource采用名字属性,默认情况下Spring将名字值作为要注入的bean的名字。换句话说,它遵循by-name语义,下面的例子证实了这一点:

1
2
3
4
5
6
7
8
9
10
public class SimpleMovieLister {

private MovieFinder movieFinder;

@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

}

If no name is specified explicitly, the default name is derived from the field name or setter method. In case of a field, it takes the field name; in case of a setter method, it takes the bean property name. So the following example is going to have the bean with name “movieFinder” injected into its setter method:

如果没有显式的指定名字,默认名字从字段名或setter方法中取得。在字段情况下,它采用字段名称;在setter方法情况下,它采用bean的属性名。因此下面的例子将名字为movieFinder的bean注入到它的setter方法中:

1
2
3
4
5
6
7
8
9
10
public class SimpleMovieLister {

private MovieFinder movieFinder;

@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

}

The name provided with the annotation is resolved as a bean name by the ApplicationContext of which the CommonAnnotationBeanPostProcessor is aware. The names can be resolved through JNDI if you configure Spring’s SimpleJndiBeanFactory explicitly. However, it is recommended that you rely on the default behavior and simply use Spring’s JNDI lookup capabilities to preserve the level of indirection.

 

注解提供的名字被CommonAnnotationBeanPostProcessor感知的ApplicationContext解析为bean名字。如果你显式地配置了Spring的SimpleJndiBeanFactory,名字会通过JNDI解析。但是建议你依赖默认行为,简单使用Spring的JNDI查找功能保护间接查找级别。

In the exclusive case of @Resource usage with no explicit name specified, and similar to @Autowired, @Resource finds a primary type match instead of a specific named bean and resolves well-known resolvable dependencies: the BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, and MessageSource interfaces.

@Resource特有的没有显式名字指定的情况下,类似于@Autowired@Resource会进行主要的匹配类型来代替指定名字的bean并解析已知的可解析依赖:BeanFactoryApplicationContextResourceLoaderApplicationEventPublisherMessageSource接口。

Thus in the following example, the customerPreferenceDao field first looks for a bean named customerPreferenceDao, then falls back to a primary type match for the type CustomerPreferenceDao. The “context” field is injected based on the known resolvable dependency type ApplicationContext.

因此在下面的例子中,customerPreferenceDao字段首先查找名字为customerPreferenceDao的bean,然后回退到主要的类型为CustomerPreferenceDao的类型匹配。”context”字段会注入基于已知的可解析依赖类型ApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MovieRecommender {

@Resource
private CustomerPreferenceDao customerPreferenceDao;

@Resource
private ApplicationContext context;

public MovieRecommender() {
}

// ...

}

3.9.8 @PostConstruct and @PreDestroy

The CommonAnnotationBeanPostProcessor not only recognizes the @Resource annotation but also the JSR-250 lifecycle annotations. Introduced in Spring 2.5, the support for these annotations offers yet another alternative to those described in initialization callbacks and destruction callbacks. Provided that the CommonAnnotationBeanPostProcessor is registered within the Spring ApplicationContext, a method carrying one of these annotations is invoked at the same point in the lifecycle as the corresponding Spring lifecycle interface method or explicitly declared callback method. In the example below, the cache will be pre-populated upon initialization and cleared upon destruction.

CommonAnnotationBeanPostProcessor不仅识别@Resource注解,而且识别JSR-250生命周期注解。在Spring 2.5引入了对这些注解的支持,也提供了在初始化回调函数和销毁回调函数中描述的那些注解的一种可替代方式。假设CommonAnnotationBeanPostProcessor在Spring的ApplicationContext中注册,执行这些注解的方法在生命周期的同一点被调用,作为对应的Spring生命周期接口方法或显式声明的回调方法。在下面的例子中,缓存会预先放置接近初始化之前,并在销毁之前清除。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CachingMovieLister {

@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}

@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}

}

For details about the effects of combining various lifecycle mechanisms, see the section called “Combining lifecycle mechanisms”.

 

关于组合各种生命周期机制的影响的更多细节,请看“组合生命周期机制”小节。

如果有收获,可以请我喝杯咖啡!